xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
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/resource.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <sys/uio.h>
38 
39 #include <aio.h>
40 #include <fcntl.h>
41 #include <signal.h>
42 #include <unistd.h>
43 }
44 
45 #include "mockfs.hh"
46 #include "utils.hh"
47 
48 using namespace testing;
49 
50 class Write: public FuseTest {
51 
52 public:
53 void SetUp() {
54 	FuseTest::SetUp();
55 }
56 
57 void TearDown() {
58 	struct sigaction sa;
59 
60 	bzero(&sa, sizeof(sa));
61 	sa.sa_handler = SIG_DFL;
62 	sigaction(SIGXFSZ, &sa, NULL);
63 
64 	FuseTest::TearDown();
65 }
66 
67 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
68 {
69 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
70 }
71 
72 void expect_release(uint64_t ino, ProcessMockerT r)
73 {
74 	EXPECT_CALL(*m_mock, process(
75 		ResultOf([=](auto in) {
76 			return (in.header.opcode == FUSE_RELEASE &&
77 				in.header.nodeid == ino);
78 		}, Eq(true)),
79 		_)
80 	).WillRepeatedly(Invoke(r));
81 }
82 
83 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
84 	uint64_t osize, const void *contents)
85 {
86 	FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
87 }
88 
89 /* Expect a write that may or may not come, depending on the cache mode */
90 void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
91 	const void *contents)
92 {
93 	EXPECT_CALL(*m_mock, process(
94 		ResultOf([=](auto in) {
95 			const char *buf = (const char*)in.body.bytes +
96 				sizeof(struct fuse_write_in);
97 
98 			assert(size <= sizeof(in.body.bytes) -
99 				sizeof(struct fuse_write_in));
100 			return (in.header.opcode == FUSE_WRITE &&
101 				in.header.nodeid == ino &&
102 				in.body.write.offset == offset  &&
103 				in.body.write.size == size &&
104 				0 == bcmp(buf, contents, size));
105 		}, Eq(true)),
106 		_)
107 	).Times(AtMost(1))
108 	.WillRepeatedly(Invoke(
109 		ReturnImmediate([=](auto in __unused, auto& out) {
110 			SET_OUT_HEADER_LEN(out, write);
111 			out.body.write.size = size;
112 		})
113 	));
114 }
115 
116 };
117 
118 class Write_7_8: public FuseTest {
119 
120 public:
121 virtual void SetUp() {
122 	m_kernel_minor_version = 8;
123 	FuseTest::SetUp();
124 }
125 
126 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
127 {
128 	FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
129 }
130 
131 };
132 
133 class AioWrite: public Write {
134 virtual void SetUp() {
135 	if (!is_unsafe_aio_enabled())
136 		GTEST_SKIP() <<
137 			"vfs.aio.enable_unsafe must be set for this test";
138 	FuseTest::SetUp();
139 }
140 };
141 
142 /* Tests for the writeback cache mode */
143 class WriteBack: public Write {
144 public:
145 virtual void SetUp() {
146 	m_init_flags |= FUSE_WRITEBACK_CACHE;
147 	FuseTest::SetUp();
148 	if (IsSkipped())
149 		return;
150 }
151 
152 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
153 	uint64_t osize, const void *contents)
154 {
155 	FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
156 		contents);
157 }
158 };
159 
160 class WriteBackAsync: public WriteBack {
161 public:
162 virtual void SetUp() {
163 	m_async = true;
164 	m_maxwrite = 65536;
165 	WriteBack::SetUp();
166 }
167 };
168 
169 class TimeGran: public WriteBackAsync, public WithParamInterface<unsigned> {
170 public:
171 virtual void SetUp() {
172 	m_time_gran = 1 << GetParam();
173 	WriteBackAsync::SetUp();
174 }
175 };
176 
177 /* Tests for clustered writes with WriteBack cacheing */
178 class WriteCluster: public WriteBack {
179 public:
180 virtual void SetUp() {
181 	m_async = true;
182 	m_maxwrite = 1 << 25;	// Anything larger than MAXPHYS will suffice
183 	WriteBack::SetUp();
184 	if (m_maxphys < 2 * DFLTPHYS)
185 		GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
186 			<< " for this test";
187 	if (m_maxphys < 2 * m_maxbcachebuf)
188 		GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf"
189 			<< " for this test";
190 }
191 };
192 
193 /* Tests relating to the server's max_write property */
194 class WriteMaxWrite: public Write {
195 public:
196 virtual void SetUp() {
197 	/*
198 	 * For this test, m_maxwrite must be less than either m_maxbcachebuf or
199 	 * maxphys.
200 	 */
201 	m_maxwrite = 32768;
202 	Write::SetUp();
203 }
204 };
205 
206 class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int>
207 {};
208 
209 class WriteRlimitFsize: public Write, public WithParamInterface<int> {
210 public:
211 static sig_atomic_t s_sigxfsz;
212 struct rlimit	m_initial_limit;
213 
214 void SetUp() {
215 	s_sigxfsz = 0;
216 	getrlimit(RLIMIT_FSIZE, &m_initial_limit);
217 	FuseTest::SetUp();
218 }
219 
220 void TearDown() {
221 	setrlimit(RLIMIT_FSIZE, &m_initial_limit);
222 
223 	FuseTest::TearDown();
224 }
225 };
226 
227 sig_atomic_t WriteRlimitFsize::s_sigxfsz = 0;
228 
229 void sigxfsz_handler(int __unused sig) {
230 	WriteRlimitFsize::s_sigxfsz = 1;
231 }
232 
233 /* AIO writes need to set the header's pid field correctly */
234 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
235 TEST_F(AioWrite, DISABLED_aio_write)
236 {
237 	const char FULLPATH[] = "mountpoint/some_file.txt";
238 	const char RELPATH[] = "some_file.txt";
239 	const char *CONTENTS = "abcdefgh";
240 	uint64_t ino = 42;
241 	uint64_t offset = 4096;
242 	int fd;
243 	ssize_t bufsize = strlen(CONTENTS);
244 	struct aiocb iocb, *piocb;
245 
246 	expect_lookup(RELPATH, ino, 0);
247 	expect_open(ino, 0, 1);
248 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
249 
250 	fd = open(FULLPATH, O_WRONLY);
251 	ASSERT_LE(0, fd) << strerror(errno);
252 
253 	iocb.aio_nbytes = bufsize;
254 	iocb.aio_fildes = fd;
255 	iocb.aio_buf = __DECONST(void *, CONTENTS);
256 	iocb.aio_offset = offset;
257 	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
258 	ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
259 	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
260 	leak(fd);
261 }
262 
263 /*
264  * When a file is opened with O_APPEND, we should forward that flag to
265  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
266  * offset internally.  That way we'll work both with filesystems that
267  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
268  * simply use the offset).
269  *
270  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
271  * Open.o_append test.
272  */
273 TEST_F(Write, append)
274 {
275 	const ssize_t BUFSIZE = 9;
276 	const char FULLPATH[] = "mountpoint/some_file.txt";
277 	const char RELPATH[] = "some_file.txt";
278 	const char CONTENTS[BUFSIZE] = "abcdefgh";
279 	uint64_t ino = 42;
280 	/*
281 	 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
282 	 * using writeback caching
283 	 */
284 	uint64_t initial_offset = m_maxbcachebuf;
285 	int fd;
286 
287 	expect_lookup(RELPATH, ino, initial_offset);
288 	expect_open(ino, 0, 1);
289 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
290 
291 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
292 	fd = open(FULLPATH, O_RDWR | O_APPEND);
293 	ASSERT_LE(0, fd) << strerror(errno);
294 
295 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
296 	leak(fd);
297 }
298 
299 /* If a file is cached, then appending to the end should not cause a read */
300 TEST_F(Write, append_to_cached)
301 {
302 	const ssize_t BUFSIZE = 9;
303 	const char FULLPATH[] = "mountpoint/some_file.txt";
304 	const char RELPATH[] = "some_file.txt";
305 	char *oldcontents, *oldbuf;
306 	const char CONTENTS[BUFSIZE] = "abcdefgh";
307 	uint64_t ino = 42;
308 	/*
309 	 * Set offset in between maxbcachebuf boundary to test buffer handling
310 	 */
311 	uint64_t oldsize = m_maxbcachebuf / 2;
312 	int fd;
313 
314 	oldcontents = new char[oldsize]();
315 	oldbuf = new char[oldsize];
316 
317 	expect_lookup(RELPATH, ino, oldsize);
318 	expect_open(ino, 0, 1);
319 	expect_read(ino, 0, oldsize, oldsize, oldcontents);
320 	maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
321 
322 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
323 	fd = open(FULLPATH, O_RDWR | O_APPEND);
324 	ASSERT_LE(0, fd) << strerror(errno);
325 
326 	/* Read the old data into the cache */
327 	ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
328 		<< strerror(errno);
329 
330 	/* Write the new data.  There should be no more read operations */
331 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
332 	leak(fd);
333 	delete[] oldbuf;
334 	delete[] oldcontents;
335 }
336 
337 TEST_F(Write, append_direct_io)
338 {
339 	const ssize_t BUFSIZE = 9;
340 	const char FULLPATH[] = "mountpoint/some_file.txt";
341 	const char RELPATH[] = "some_file.txt";
342 	const char CONTENTS[BUFSIZE] = "abcdefgh";
343 	uint64_t ino = 42;
344 	uint64_t initial_offset = 4096;
345 	int fd;
346 
347 	expect_lookup(RELPATH, ino, initial_offset);
348 	expect_open(ino, FOPEN_DIRECT_IO, 1);
349 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
350 
351 	fd = open(FULLPATH, O_WRONLY | O_APPEND);
352 	ASSERT_LE(0, fd) << strerror(errno);
353 
354 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
355 	leak(fd);
356 }
357 
358 /* A direct write should evict any overlapping cached data */
359 TEST_F(Write, direct_io_evicts_cache)
360 {
361 	const char FULLPATH[] = "mountpoint/some_file.txt";
362 	const char RELPATH[] = "some_file.txt";
363 	const char CONTENTS0[] = "abcdefgh";
364 	const char CONTENTS1[] = "ijklmnop";
365 	uint64_t ino = 42;
366 	int fd;
367 	ssize_t bufsize = strlen(CONTENTS0) + 1;
368 	char readbuf[bufsize];
369 
370 	expect_lookup(RELPATH, ino, bufsize);
371 	expect_open(ino, 0, 1);
372 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
373 	expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
374 
375 	fd = open(FULLPATH, O_RDWR);
376 	ASSERT_LE(0, fd) << strerror(errno);
377 
378 	// Prime cache
379 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
380 
381 	// Write directly, evicting cache
382 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
383 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
384 	ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
385 
386 	// Read again.  Cache should be bypassed
387 	expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
388 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
389 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
390 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
391 	ASSERT_STREQ(readbuf, CONTENTS1);
392 
393 	leak(fd);
394 }
395 
396 /*
397  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
398  * allowed to return a short write for that file handle.  However, if it does
399  * then we should still do our darndest to handle it by resending the unwritten
400  * portion.
401  */
402 TEST_F(Write, indirect_io_short_write)
403 {
404 	const char FULLPATH[] = "mountpoint/some_file.txt";
405 	const char RELPATH[] = "some_file.txt";
406 	const char *CONTENTS = "abcdefghijklmnop";
407 	uint64_t ino = 42;
408 	int fd;
409 	ssize_t bufsize = strlen(CONTENTS);
410 	ssize_t bufsize0 = 11;
411 	ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
412 	const char *contents1 = CONTENTS + bufsize0;
413 
414 	expect_lookup(RELPATH, ino, 0);
415 	expect_open(ino, 0, 1);
416 	expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
417 	expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
418 
419 	fd = open(FULLPATH, O_WRONLY);
420 	ASSERT_LE(0, fd) << strerror(errno);
421 
422 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
423 	leak(fd);
424 }
425 
426 /* It is an error if the daemon claims to have written more data than we sent */
427 TEST_F(Write, indirect_io_long_write)
428 {
429 	const char FULLPATH[] = "mountpoint/some_file.txt";
430 	const char RELPATH[] = "some_file.txt";
431 	const char *CONTENTS = "abcdefghijklmnop";
432 	uint64_t ino = 42;
433 	int fd;
434 	ssize_t bufsize = strlen(CONTENTS);
435 	ssize_t bufsize_out = 100;
436 	off_t some_other_size = 25;
437 	struct stat sb;
438 
439 	expect_lookup(RELPATH, ino, 0);
440 	expect_open(ino, 0, 1);
441 	expect_write(ino, 0, bufsize, bufsize_out, CONTENTS);
442 	expect_getattr(ino, some_other_size);
443 
444 	fd = open(FULLPATH, O_WRONLY);
445 	ASSERT_LE(0, fd) << strerror(errno);
446 
447 	ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno);
448 	ASSERT_EQ(EINVAL, errno);
449 
450 	/*
451 	 * Following such an error, we should requery the server for the file's
452 	 * size.
453 	 */
454 	fstat(fd, &sb);
455 	ASSERT_EQ(sb.st_size, some_other_size);
456 
457 	leak(fd);
458 }
459 
460 /*
461  * Don't crash if the server returns a write that can't be represented as a
462  * signed 32 bit number.  Regression test for
463  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263263
464  */
465 TEST_F(Write, indirect_io_very_long_write)
466 {
467 	const char FULLPATH[] = "mountpoint/some_file.txt";
468 	const char RELPATH[] = "some_file.txt";
469 	const char *CONTENTS = "abcdefghijklmnop";
470 	uint64_t ino = 42;
471 	int fd;
472 	ssize_t bufsize = strlen(CONTENTS);
473 	ssize_t bufsize_out = 3 << 30;
474 
475 	expect_lookup(RELPATH, ino, 0);
476 	expect_open(ino, 0, 1);
477 	expect_write(ino, 0, bufsize, bufsize_out, CONTENTS);
478 
479 	fd = open(FULLPATH, O_WRONLY);
480 	ASSERT_LE(0, fd) << strerror(errno);
481 
482 	ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno);
483 	ASSERT_EQ(EINVAL, errno);
484 	leak(fd);
485 }
486 
487 /*
488  * When the direct_io option is used, filesystems are allowed to write less
489  * data than requested.  We should return the short write to userland.
490  */
491 TEST_F(Write, direct_io_short_write)
492 {
493 	const char FULLPATH[] = "mountpoint/some_file.txt";
494 	const char RELPATH[] = "some_file.txt";
495 	const char *CONTENTS = "abcdefghijklmnop";
496 	uint64_t ino = 42;
497 	int fd;
498 	ssize_t bufsize = strlen(CONTENTS);
499 	ssize_t halfbufsize = bufsize / 2;
500 
501 	expect_lookup(RELPATH, ino, 0);
502 	expect_open(ino, FOPEN_DIRECT_IO, 1);
503 	expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
504 
505 	fd = open(FULLPATH, O_WRONLY);
506 	ASSERT_LE(0, fd) << strerror(errno);
507 
508 	ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
509 	leak(fd);
510 }
511 
512 /*
513  * An insidious edge case: the filesystem returns a short write, and the
514  * difference between what we requested and what it actually wrote crosses an
515  * iov element boundary
516  */
517 TEST_F(Write, direct_io_short_write_iov)
518 {
519 	const char FULLPATH[] = "mountpoint/some_file.txt";
520 	const char RELPATH[] = "some_file.txt";
521 	const char *CONTENTS0 = "abcdefgh";
522 	const char *CONTENTS1 = "ijklmnop";
523 	const char *EXPECTED0 = "abcdefghijklmnop";
524 	uint64_t ino = 42;
525 	int fd;
526 	ssize_t size0 = strlen(CONTENTS0) - 1;
527 	ssize_t size1 = strlen(CONTENTS1) + 1;
528 	ssize_t totalsize = size0 + size1;
529 	struct iovec iov[2];
530 
531 	expect_lookup(RELPATH, ino, 0);
532 	expect_open(ino, FOPEN_DIRECT_IO, 1);
533 	expect_write(ino, 0, totalsize, size0, EXPECTED0);
534 
535 	fd = open(FULLPATH, O_WRONLY);
536 	ASSERT_LE(0, fd) << strerror(errno);
537 
538 	iov[0].iov_base = __DECONST(void*, CONTENTS0);
539 	iov[0].iov_len = strlen(CONTENTS0);
540 	iov[1].iov_base = __DECONST(void*, CONTENTS1);
541 	iov[1].iov_len = strlen(CONTENTS1);
542 	ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
543 	leak(fd);
544 }
545 
546 /* fusefs should respect RLIMIT_FSIZE */
547 TEST_P(WriteRlimitFsize, rlimit_fsize)
548 {
549 	const char FULLPATH[] = "mountpoint/some_file.txt";
550 	const char RELPATH[] = "some_file.txt";
551 	const char *CONTENTS = "abcdefgh";
552 	struct rlimit rl;
553 	ssize_t bufsize = strlen(CONTENTS);
554 	off_t offset = 1'000'000'000;
555 	uint64_t ino = 42;
556 	int fd, oflag;
557 
558 	oflag = GetParam();
559 
560 	expect_lookup(RELPATH, ino, 0);
561 	expect_open(ino, 0, 1);
562 
563 	rl.rlim_cur = offset;
564 	rl.rlim_max = m_initial_limit.rlim_max;
565 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
566 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
567 
568 	fd = open(FULLPATH, O_WRONLY | oflag);
569 
570 	ASSERT_LE(0, fd) << strerror(errno);
571 
572 	ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
573 	EXPECT_EQ(EFBIG, errno);
574 	EXPECT_EQ(1, s_sigxfsz);
575 	leak(fd);
576 }
577 
578 /*
579  * When crossing the RLIMIT_FSIZE boundary, writes should be truncated, not
580  * aborted.
581  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=164793
582  */
583 TEST_P(WriteRlimitFsize, rlimit_fsize_truncate)
584 {
585 	const char FULLPATH[] = "mountpoint/some_file.txt";
586 	const char RELPATH[] = "some_file.txt";
587 	const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
588 	struct rlimit rl;
589 	ssize_t bufsize = strlen(CONTENTS);
590 	uint64_t ino = 42;
591 	off_t offset = 1 << 30;
592 	off_t limit = offset + strlen(CONTENTS) / 2;
593 	int fd, oflag;
594 
595 	oflag = GetParam();
596 
597 	expect_lookup(RELPATH, ino, 0);
598 	expect_open(ino, 0, 1);
599 	expect_write(ino, offset, bufsize / 2, bufsize / 2, CONTENTS);
600 
601 	rl.rlim_cur = limit;
602 	rl.rlim_max = m_initial_limit.rlim_max;
603 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
604 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
605 
606 	fd = open(FULLPATH, O_WRONLY | oflag);
607 
608 	ASSERT_LE(0, fd) << strerror(errno);
609 
610 	ASSERT_EQ(bufsize / 2, pwrite(fd, CONTENTS, bufsize, offset))
611 		<< strerror(errno);
612 	leak(fd);
613 }
614 
615 INSTANTIATE_TEST_SUITE_P(W, WriteRlimitFsize,
616 	Values(0, O_DIRECT)
617 );
618 
619 /*
620  * A short read indicates EOF.  Test that nothing bad happens if we get EOF
621  * during the R of a RMW operation.
622  */
623 TEST_F(Write, eof_during_rmw)
624 {
625 	const char FULLPATH[] = "mountpoint/some_file.txt";
626 	const char RELPATH[] = "some_file.txt";
627 	const char *CONTENTS = "abcdefgh";
628 	const char *INITIAL   = "XXXXXXXXXX";
629 	uint64_t ino = 42;
630 	uint64_t offset = 1;
631 	ssize_t bufsize = strlen(CONTENTS) + 1;
632 	off_t orig_fsize = 10;
633 	off_t truncated_fsize = 5;
634 	int fd;
635 
636 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
637 	expect_open(ino, 0, 1);
638 	expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
639 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
640 
641 	fd = open(FULLPATH, O_RDWR);
642 	ASSERT_LE(0, fd) << strerror(errno);
643 
644 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
645 		<< strerror(errno);
646 	leak(fd);
647 }
648 
649 /*
650  * VOP_STRATEGY should not query the server for the file's size, even if its
651  * cached attributes have expired.
652  * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
653  */
654 TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy)
655 {
656 	const char FULLPATH[] = "mountpoint/some_file.txt";
657 	const char RELPATH[] = "some_file.txt";
658 	Sequence seq;
659 	const off_t filesize = 2 * m_maxbcachebuf;
660 	char *contents;
661 	uint64_t ino = 42;
662 	uint64_t attr_valid = 0;
663 	uint64_t attr_valid_nsec = 0;
664 	mode_t mode = S_IFREG | 0644;
665 	int fd;
666 	int ngetattrs;
667 
668 	ngetattrs = GetParam();
669 	contents = new char[filesize]();
670 
671 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
672 	.WillRepeatedly(Invoke(
673 		ReturnImmediate([=](auto in __unused, auto& out) {
674 		SET_OUT_HEADER_LEN(out, entry);
675 		out.body.entry.attr.mode = mode;
676 		out.body.entry.nodeid = ino;
677 		out.body.entry.attr.nlink = 1;
678 		out.body.entry.attr.size = filesize;
679 		out.body.entry.attr_valid = attr_valid;
680 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
681 	})));
682 	expect_open(ino, 0, 1);
683 	EXPECT_CALL(*m_mock, process(
684 		ResultOf([=](auto in) {
685 			return (in.header.opcode == FUSE_GETATTR &&
686 				in.header.nodeid == ino);
687 		}, Eq(true)),
688 		_)
689 	).Times(Between(ngetattrs - 1, ngetattrs))
690 	.InSequence(seq)
691 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
692 		SET_OUT_HEADER_LEN(out, attr);
693 		out.body.attr.attr.ino = ino;
694 		out.body.attr.attr.mode = mode;
695 		out.body.attr.attr_valid = attr_valid;
696 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
697 		out.body.attr.attr.size = filesize;
698 	})));
699 	EXPECT_CALL(*m_mock, process(
700 		ResultOf([=](auto in) {
701 			return (in.header.opcode == FUSE_GETATTR &&
702 				in.header.nodeid == ino);
703 		}, Eq(true)),
704 		_)
705 	).InSequence(seq)
706 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
707 		SET_OUT_HEADER_LEN(out, attr);
708 		out.body.attr.attr.ino = ino;
709 		out.body.attr.attr.mode = mode;
710 		out.body.attr.attr_valid = attr_valid;
711 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
712 		out.body.attr.attr.size = filesize / 2;
713 	})));
714 	expect_write(ino, 0, filesize / 2, filesize / 2, contents);
715 
716 	fd = open(FULLPATH, O_RDWR);
717 	ASSERT_LE(0, fd) << strerror(errno);
718 	ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2))
719 		<< strerror(errno);
720 
721 }
722 
723 INSTANTIATE_TEST_SUITE_P(W, WriteEofDuringVnopStrategy,
724 	Values(1, 2, 3)
725 );
726 
727 /*
728  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
729  * write, then it must set the FUSE_WRITE_CACHE bit
730  */
731 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
732 TEST_F(Write, mmap)
733 {
734 	const char FULLPATH[] = "mountpoint/some_file.txt";
735 	const char RELPATH[] = "some_file.txt";
736 	const char *CONTENTS = "abcdefgh";
737 	uint64_t ino = 42;
738 	int fd;
739 	ssize_t bufsize = strlen(CONTENTS);
740 	void *p;
741 	uint64_t offset = 10;
742 	size_t len;
743 	char *zeros, *expected;
744 
745 	len = getpagesize();
746 
747 	zeros = new char[len]();
748 	expected = new char[len]();
749 	memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
750 
751 	expect_lookup(RELPATH, ino, len);
752 	expect_open(ino, 0, 1);
753 	expect_read(ino, 0, len, len, zeros);
754 	/*
755 	 * Writes from the pager may or may not be associated with the correct
756 	 * pid, so they must set FUSE_WRITE_CACHE.
757 	 */
758 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
759 	expect_flush(ino, 1, ReturnErrno(0));
760 	expect_release(ino, ReturnErrno(0));
761 
762 	fd = open(FULLPATH, O_RDWR);
763 	ASSERT_LE(0, fd) << strerror(errno);
764 
765 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
766 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
767 
768 	memmove((uint8_t*)p + offset, CONTENTS, bufsize);
769 
770 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
771 	close(fd);	// Write mmap'd data on close
772 
773 	delete[] expected;
774 	delete[] zeros;
775 
776 	leak(fd);
777 }
778 
779 TEST_F(Write, pwrite)
780 {
781 	const char FULLPATH[] = "mountpoint/some_file.txt";
782 	const char RELPATH[] = "some_file.txt";
783 	const char *CONTENTS = "abcdefgh";
784 	uint64_t ino = 42;
785 	uint64_t offset = m_maxbcachebuf;
786 	int fd;
787 	ssize_t bufsize = strlen(CONTENTS);
788 
789 	expect_lookup(RELPATH, ino, 0);
790 	expect_open(ino, 0, 1);
791 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
792 
793 	fd = open(FULLPATH, O_WRONLY);
794 	ASSERT_LE(0, fd) << strerror(errno);
795 
796 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
797 		<< strerror(errno);
798 	leak(fd);
799 }
800 
801 /* Writing a file should update its cached mtime and ctime */
802 TEST_F(Write, timestamps)
803 {
804 	const char FULLPATH[] = "mountpoint/some_file.txt";
805 	const char RELPATH[] = "some_file.txt";
806 	const char *CONTENTS = "abcdefgh";
807 	ssize_t bufsize = strlen(CONTENTS);
808 	uint64_t ino = 42;
809 	struct stat sb0, sb1;
810 	int fd;
811 
812 	expect_lookup(RELPATH, ino, 0);
813 	expect_open(ino, 0, 1);
814 	maybe_expect_write(ino, 0, bufsize, CONTENTS);
815 
816 	fd = open(FULLPATH, O_RDWR);
817 	ASSERT_LE(0, fd) << strerror(errno);
818 	ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
819 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
820 
821 	nap();
822 
823 	ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno);
824 
825 	EXPECT_EQ(sb0.st_atime, sb1.st_atime);
826 	EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
827 	EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
828 
829 	leak(fd);
830 }
831 
832 TEST_F(Write, write)
833 {
834 	const char FULLPATH[] = "mountpoint/some_file.txt";
835 	const char RELPATH[] = "some_file.txt";
836 	const char *CONTENTS = "abcdefgh";
837 	uint64_t ino = 42;
838 	int fd;
839 	ssize_t bufsize = strlen(CONTENTS);
840 
841 	expect_lookup(RELPATH, ino, 0);
842 	expect_open(ino, 0, 1);
843 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
844 
845 	fd = open(FULLPATH, O_WRONLY);
846 	ASSERT_LE(0, fd) << strerror(errno);
847 
848 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
849 	leak(fd);
850 }
851 
852 /* fuse(4) should not issue writes of greater size than the daemon requests */
853 TEST_F(WriteMaxWrite, write)
854 {
855 	const char FULLPATH[] = "mountpoint/some_file.txt";
856 	const char RELPATH[] = "some_file.txt";
857 	int *contents;
858 	uint64_t ino = 42;
859 	int fd;
860 	ssize_t halfbufsize, bufsize;
861 
862 	halfbufsize = m_mock->m_maxwrite;
863 	if (halfbufsize >= m_maxbcachebuf || halfbufsize >= m_maxphys)
864 		GTEST_SKIP() << "Must lower m_maxwrite for this test";
865 	bufsize = halfbufsize * 2;
866 	contents = new int[bufsize / sizeof(int)];
867 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
868 		contents[i] = i;
869 	}
870 
871 	expect_lookup(RELPATH, ino, 0);
872 	expect_open(ino, 0, 1);
873 	maybe_expect_write(ino, 0, halfbufsize, contents);
874 	maybe_expect_write(ino, halfbufsize, halfbufsize,
875 		&contents[halfbufsize / sizeof(int)]);
876 
877 	fd = open(FULLPATH, O_WRONLY);
878 	ASSERT_LE(0, fd) << strerror(errno);
879 
880 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
881 	leak(fd);
882 
883 	delete[] contents;
884 }
885 
886 TEST_F(Write, write_nothing)
887 {
888 	const char FULLPATH[] = "mountpoint/some_file.txt";
889 	const char RELPATH[] = "some_file.txt";
890 	const char *CONTENTS = "";
891 	uint64_t ino = 42;
892 	int fd;
893 	ssize_t bufsize = 0;
894 
895 	expect_lookup(RELPATH, ino, 0);
896 	expect_open(ino, 0, 1);
897 
898 	fd = open(FULLPATH, O_WRONLY);
899 	ASSERT_LE(0, fd) << strerror(errno);
900 
901 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
902 	leak(fd);
903 }
904 
905 TEST_F(Write_7_8, write)
906 {
907 	const char FULLPATH[] = "mountpoint/some_file.txt";
908 	const char RELPATH[] = "some_file.txt";
909 	const char *CONTENTS = "abcdefgh";
910 	uint64_t ino = 42;
911 	int fd;
912 	ssize_t bufsize = strlen(CONTENTS);
913 
914 	expect_lookup(RELPATH, ino, 0);
915 	expect_open(ino, 0, 1);
916 	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
917 
918 	fd = open(FULLPATH, O_WRONLY);
919 	ASSERT_LE(0, fd) << strerror(errno);
920 
921 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
922 	leak(fd);
923 }
924 
925 /* In writeback mode, dirty data should be written on close */
926 TEST_F(WriteBackAsync, close)
927 {
928 	const char FULLPATH[] = "mountpoint/some_file.txt";
929 	const char RELPATH[] = "some_file.txt";
930 	const char *CONTENTS = "abcdefgh";
931 	uint64_t ino = 42;
932 	int fd;
933 	ssize_t bufsize = strlen(CONTENTS);
934 
935 	expect_lookup(RELPATH, ino, 0);
936 	expect_open(ino, 0, 1);
937 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
938 	EXPECT_CALL(*m_mock, process(
939 		ResultOf([=](auto in) {
940 			return (in.header.opcode == FUSE_SETATTR);
941 		}, Eq(true)),
942 		_)
943 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
944 		SET_OUT_HEADER_LEN(out, attr);
945 		out.body.attr.attr.ino = ino;	// Must match nodeid
946 	})));
947 	expect_flush(ino, 1, ReturnErrno(0));
948 	expect_release(ino, ReturnErrno(0));
949 
950 	fd = open(FULLPATH, O_RDWR);
951 	ASSERT_LE(0, fd) << strerror(errno);
952 
953 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
954 	close(fd);
955 }
956 
957 /* In writeback mode, adjacent writes will be clustered together */
958 TEST_F(WriteCluster, clustering)
959 {
960 	const char FULLPATH[] = "mountpoint/some_file.txt";
961 	const char RELPATH[] = "some_file.txt";
962 	uint64_t ino = 42;
963 	int i, fd;
964 	char *wbuf, *wbuf2x;
965 	ssize_t bufsize = m_maxbcachebuf;
966 	off_t filesize = 5 * bufsize;
967 
968 	wbuf = new char[bufsize];
969 	memset(wbuf, 'X', bufsize);
970 	wbuf2x = new char[2 * bufsize];
971 	memset(wbuf2x, 'X', 2 * bufsize);
972 
973 	expect_lookup(RELPATH, ino, filesize);
974 	expect_open(ino, 0, 1);
975 	/*
976 	 * Writes of bufsize-bytes each should be clustered into greater sizes.
977 	 * The amount of clustering is adaptive, so the first write actually
978 	 * issued will be 2x bufsize and subsequent writes may be larger
979 	 */
980 	expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
981 	expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
982 	expect_flush(ino, 1, ReturnErrno(0));
983 	expect_release(ino, ReturnErrno(0));
984 
985 	fd = open(FULLPATH, O_RDWR);
986 	ASSERT_LE(0, fd) << strerror(errno);
987 
988 	for (i = 0; i < 4; i++) {
989 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
990 			<< strerror(errno);
991 	}
992 	close(fd);
993 	delete[] wbuf2x;
994 	delete[] wbuf;
995 }
996 
997 /*
998  * When clustering writes, an I/O error to any of the cluster's children should
999  * not panic the system on unmount
1000  */
1001 /*
1002  * Regression test for bug 238585
1003  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
1004  */
1005 TEST_F(WriteCluster, cluster_write_err)
1006 {
1007 	const char FULLPATH[] = "mountpoint/some_file.txt";
1008 	const char RELPATH[] = "some_file.txt";
1009 	uint64_t ino = 42;
1010 	int i, fd;
1011 	char *wbuf;
1012 	ssize_t bufsize = m_maxbcachebuf;
1013 	off_t filesize = 4 * bufsize;
1014 
1015 	wbuf = new char[bufsize];
1016 	memset(wbuf, 'X', bufsize);
1017 
1018 	expect_lookup(RELPATH, ino, filesize);
1019 	expect_open(ino, 0, 1);
1020 	EXPECT_CALL(*m_mock, process(
1021 		ResultOf([=](auto in) {
1022 			return (in.header.opcode == FUSE_WRITE);
1023 		}, Eq(true)),
1024 		_)
1025 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
1026 	expect_flush(ino, 1, ReturnErrno(0));
1027 	expect_release(ino, ReturnErrno(0));
1028 
1029 	fd = open(FULLPATH, O_RDWR);
1030 	ASSERT_LE(0, fd) << strerror(errno);
1031 
1032 	for (i = 0; i < 3; i++) {
1033 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
1034 			<< strerror(errno);
1035 	}
1036 	close(fd);
1037 	delete[] wbuf;
1038 }
1039 
1040 /*
1041  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
1042  * server.  The FUSE protocol explicitly allows that.
1043  */
1044 TEST_F(WriteBack, rmw)
1045 {
1046 	const char FULLPATH[] = "mountpoint/some_file.txt";
1047 	const char RELPATH[] = "some_file.txt";
1048 	const char *CONTENTS = "abcdefgh";
1049 	const char *INITIAL   = "XXXXXXXXXX";
1050 	uint64_t ino = 42;
1051 	uint64_t offset = 1;
1052 	off_t fsize = 10;
1053 	int fd;
1054 	ssize_t bufsize = strlen(CONTENTS);
1055 
1056 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
1057 	expect_open(ino, 0, 1);
1058 	expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
1059 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
1060 
1061 	fd = open(FULLPATH, O_WRONLY);
1062 	ASSERT_LE(0, fd) << strerror(errno);
1063 
1064 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
1065 		<< strerror(errno);
1066 	leak(fd);
1067 }
1068 
1069 /*
1070  * Without direct_io, writes should be committed to cache
1071  */
1072 TEST_F(WriteBack, cache)
1073 {
1074 	const char FULLPATH[] = "mountpoint/some_file.txt";
1075 	const char RELPATH[] = "some_file.txt";
1076 	const char *CONTENTS = "abcdefgh";
1077 	uint64_t ino = 42;
1078 	int fd;
1079 	ssize_t bufsize = strlen(CONTENTS);
1080 	uint8_t readbuf[bufsize];
1081 
1082 	expect_lookup(RELPATH, ino, 0);
1083 	expect_open(ino, 0, 1);
1084 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
1085 
1086 	fd = open(FULLPATH, O_RDWR);
1087 	ASSERT_LE(0, fd) << strerror(errno);
1088 
1089 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1090 	/*
1091 	 * A subsequent read should be serviced by cache, without querying the
1092 	 * filesystem daemon
1093 	 */
1094 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1095 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1096 	leak(fd);
1097 }
1098 
1099 /*
1100  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
1101  * an odd test, because it would be unusual to use O_DIRECT for writes but not
1102  * reads.
1103  */
1104 TEST_F(WriteBack, o_direct)
1105 {
1106 	const char FULLPATH[] = "mountpoint/some_file.txt";
1107 	const char RELPATH[] = "some_file.txt";
1108 	const char *CONTENTS = "abcdefgh";
1109 	uint64_t ino = 42;
1110 	int fd;
1111 	ssize_t bufsize = strlen(CONTENTS);
1112 	uint8_t readbuf[bufsize];
1113 
1114 	expect_lookup(RELPATH, ino, 0);
1115 	expect_open(ino, 0, 1);
1116 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1117 		CONTENTS);
1118 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1119 
1120 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
1121 	ASSERT_LE(0, fd) << strerror(errno);
1122 
1123 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1124 	/* A subsequent read must query the daemon because cache is empty */
1125 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1126 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1127 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1128 	leak(fd);
1129 }
1130 
1131 TEST_F(WriteBack, direct_io)
1132 {
1133 	const char FULLPATH[] = "mountpoint/some_file.txt";
1134 	const char RELPATH[] = "some_file.txt";
1135 	const char *CONTENTS = "abcdefgh";
1136 	uint64_t ino = 42;
1137 	int fd;
1138 	ssize_t bufsize = strlen(CONTENTS);
1139 	uint8_t readbuf[bufsize];
1140 
1141 	expect_lookup(RELPATH, ino, 0);
1142 	expect_open(ino, FOPEN_DIRECT_IO, 1);
1143 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1144 		CONTENTS);
1145 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1146 
1147 	fd = open(FULLPATH, O_RDWR);
1148 	ASSERT_LE(0, fd) << strerror(errno);
1149 
1150 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1151 	/* A subsequent read must query the daemon because cache is empty */
1152 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1153 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1154 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1155 	leak(fd);
1156 }
1157 
1158 /*
1159  * mmap should still be possible even if the server used direct_io.  Mmap will
1160  * still use the cache, though.
1161  *
1162  * Regression test for bug 247276
1163  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276
1164  */
1165 TEST_F(WriteBack, mmap_direct_io)
1166 {
1167 	const char FULLPATH[] = "mountpoint/some_file.txt";
1168 	const char RELPATH[] = "some_file.txt";
1169 	const char *CONTENTS = "abcdefgh";
1170 	uint64_t ino = 42;
1171 	int fd;
1172 	size_t len;
1173 	ssize_t bufsize = strlen(CONTENTS);
1174 	char *zeros;
1175 	void *p;
1176 
1177 	len = getpagesize();
1178 	zeros = new char[len]();
1179 
1180 	expect_lookup(RELPATH, ino, len);
1181 	expect_open(ino, FOPEN_DIRECT_IO, 1);
1182 	expect_read(ino, 0, len, len, zeros);
1183 	expect_flush(ino, 1, ReturnErrno(0));
1184 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros);
1185 	expect_release(ino, ReturnErrno(0));
1186 
1187 	fd = open(FULLPATH, O_RDWR);
1188 	ASSERT_LE(0, fd) << strerror(errno);
1189 
1190 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1191 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1192 
1193 	memmove((uint8_t*)p, CONTENTS, bufsize);
1194 
1195 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
1196 	close(fd);	// Write mmap'd data on close
1197 
1198 	delete[] zeros;
1199 }
1200 
1201 /*
1202  * When mounted with -o async, the writeback cache mode should delay writes
1203  */
1204 TEST_F(WriteBackAsync, delay)
1205 {
1206 	const char FULLPATH[] = "mountpoint/some_file.txt";
1207 	const char RELPATH[] = "some_file.txt";
1208 	const char *CONTENTS = "abcdefgh";
1209 	uint64_t ino = 42;
1210 	int fd;
1211 	ssize_t bufsize = strlen(CONTENTS);
1212 
1213 	expect_lookup(RELPATH, ino, 0);
1214 	expect_open(ino, 0, 1);
1215 	/* Write should be cached, but FUSE_WRITE shouldn't be sent */
1216 	EXPECT_CALL(*m_mock, process(
1217 		ResultOf([=](auto in) {
1218 			return (in.header.opcode == FUSE_WRITE);
1219 		}, Eq(true)),
1220 		_)
1221 	).Times(0);
1222 
1223 	fd = open(FULLPATH, O_RDWR);
1224 	ASSERT_LE(0, fd) << strerror(errno);
1225 
1226 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1227 
1228 	/* Don't close the file because that would flush the cache */
1229 	leak(fd);
1230 }
1231 
1232 /*
1233  * A direct write should not evict dirty cached data from outside of its own
1234  * byte range.
1235  */
1236 TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached)
1237 {
1238 	const char FULLPATH[] = "mountpoint/some_file.txt";
1239 	const char RELPATH[] = "some_file.txt";
1240 	const char CONTENTS0[] = "abcdefgh";
1241 	const char CONTENTS1[] = "ijklmnop";
1242 	uint64_t ino = 42;
1243 	int fd;
1244 	ssize_t bufsize = strlen(CONTENTS0) + 1;
1245 	ssize_t fsize = 2 * m_maxbcachebuf;
1246 	char readbuf[bufsize];
1247 	char *zeros;
1248 
1249 	zeros = new char[m_maxbcachebuf]();
1250 
1251 	expect_lookup(RELPATH, ino, fsize);
1252 	expect_open(ino, 0, 1);
1253 	expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros);
1254 	FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0,
1255 		CONTENTS1);
1256 
1257 	fd = open(FULLPATH, O_RDWR);
1258 	ASSERT_LE(0, fd) << strerror(errno);
1259 
1260 	// Cache first block with dirty data.  This will entail first reading
1261 	// the existing data.
1262 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0))
1263 		<< strerror(errno);
1264 
1265 	// Write directly to second block
1266 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1267 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf))
1268 		<< strerror(errno);
1269 
1270 	// Read from the first block again.  Should be serviced by cache.
1271 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1272 	ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno);
1273 	ASSERT_STREQ(readbuf, CONTENTS0);
1274 
1275 	leak(fd);
1276 	delete[] zeros;
1277 }
1278 
1279 /*
1280  * If a direct io write partially overlaps one or two blocks of dirty cached
1281  * data, No dirty data should be lost.  Admittedly this is a weird test,
1282  * because it would be unusual to use O_DIRECT and the writeback cache.
1283  */
1284 TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block)
1285 {
1286 	const char FULLPATH[] = "mountpoint/some_file.txt";
1287 	const char RELPATH[] = "some_file.txt";
1288 	uint64_t ino = 42;
1289 	int fd;
1290 	off_t bs = m_maxbcachebuf;
1291 	ssize_t fsize = 3 * bs;
1292 	char *readbuf, *zeros, *ones, *zeroones, *onezeros;
1293 
1294 	readbuf = new char[bs];
1295 	zeros = new char[3 * bs]();
1296 	ones = new char[2 * bs];
1297 	memset(ones, 1, 2 * bs);
1298 	zeroones = new char[bs]();
1299 	memset((uint8_t*)zeroones + bs / 2, 1, bs / 2);
1300 	onezeros = new char[bs]();
1301 	memset(onezeros, 1, bs / 2);
1302 
1303 	expect_lookup(RELPATH, ino, fsize);
1304 	expect_open(ino, 0, 1);
1305 
1306 	fd = open(FULLPATH, O_RDWR);
1307 	ASSERT_LE(0, fd) << strerror(errno);
1308 
1309 	/* Cache first and third blocks with dirty data.  */
1310 	ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno);
1311 
1312 	/*
1313 	 * Write directly to all three blocks.  The partially written blocks
1314 	 * will be flushed because they're dirty.
1315 	 */
1316 	FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros);
1317 	FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros);
1318 	/* The direct write is split in two because of the m_maxwrite value */
1319 	FuseTest::expect_write(ino,     bs / 2, bs, bs, 0, 0, ones);
1320 	FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones);
1321 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1322 	ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno);
1323 
1324 	/*
1325 	 * Read from both the valid and invalid portions of the first and third
1326 	 * blocks again.  This will entail FUSE_READ operations because these
1327 	 * blocks were invalidated by the direct write.
1328 	 */
1329 	expect_read(ino, 0, bs, bs, zeroones);
1330 	expect_read(ino, 2 * bs, bs, bs, onezeros);
1331 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1332 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno);
1333 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1334 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2))
1335 		<< strerror(errno);
1336 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1337 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2))
1338 		<< strerror(errno);
1339 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1340 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs))
1341 		<< strerror(errno);
1342 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1343 
1344 	leak(fd);
1345 	delete[] zeroones;
1346 	delete[] onezeros;
1347 	delete[] ones;
1348 	delete[] zeros;
1349 	delete[] readbuf;
1350 }
1351 
1352 /*
1353  * In WriteBack mode, writes may be cached beyond what the server thinks is the
1354  * EOF.  In this case, a short read at EOF should _not_ cause fusefs to update
1355  * the file's size.
1356  */
1357 TEST_F(WriteBackAsync, eof)
1358 {
1359 	const char FULLPATH[] = "mountpoint/some_file.txt";
1360 	const char RELPATH[] = "some_file.txt";
1361 	const char *CONTENTS0 = "abcdefgh";
1362 	const char *CONTENTS1 = "ijklmnop";
1363 	uint64_t ino = 42;
1364 	int fd;
1365 	off_t offset = m_maxbcachebuf;
1366 	ssize_t wbufsize = strlen(CONTENTS1);
1367 	off_t old_filesize = (off_t)strlen(CONTENTS0);
1368 	ssize_t rbufsize = 2 * old_filesize;
1369 	char readbuf[rbufsize];
1370 	size_t holesize = rbufsize - old_filesize;
1371 	char hole[holesize];
1372 	struct stat sb;
1373 	ssize_t r;
1374 
1375 	expect_lookup(RELPATH, ino, 0);
1376 	expect_open(ino, 0, 1);
1377 	expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
1378 
1379 	fd = open(FULLPATH, O_RDWR);
1380 	ASSERT_LE(0, fd) << strerror(errno);
1381 
1382 	/* Write and cache data beyond EOF */
1383 	ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
1384 		<< strerror(errno);
1385 
1386 	/* Read from the old EOF */
1387 	r = pread(fd, readbuf, rbufsize, 0);
1388 	ASSERT_LE(0, r) << strerror(errno);
1389 	EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
1390 	EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
1391 	bzero(hole, holesize);
1392 	EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
1393 
1394 	/* The file's size should still be what was established by pwrite */
1395 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1396 	EXPECT_EQ(offset + wbufsize, sb.st_size);
1397 	leak(fd);
1398 }
1399 
1400 /*
1401  * When a file has dirty writes that haven't been flushed, the server's notion
1402  * of its mtime and ctime will be wrong.  The kernel should ignore those if it
1403  * gets them from a FUSE_GETATTR before flushing.
1404  */
1405 TEST_F(WriteBackAsync, timestamps)
1406 {
1407 	const char FULLPATH[] = "mountpoint/some_file.txt";
1408 	const char RELPATH[] = "some_file.txt";
1409 	const char *CONTENTS = "abcdefgh";
1410 	ssize_t bufsize = strlen(CONTENTS);
1411 	uint64_t ino = 42;
1412 	uint64_t attr_valid = 0;
1413 	uint64_t attr_valid_nsec = 0;
1414 	uint64_t server_time = 12345;
1415 	mode_t mode = S_IFREG | 0644;
1416 	int fd;
1417 
1418 	struct stat sb;
1419 
1420 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
1421 	.WillRepeatedly(Invoke(
1422 		ReturnImmediate([=](auto in __unused, auto& out) {
1423 		SET_OUT_HEADER_LEN(out, entry);
1424 		out.body.entry.attr.mode = mode;
1425 		out.body.entry.nodeid = ino;
1426 		out.body.entry.attr.nlink = 1;
1427 		out.body.entry.attr_valid = attr_valid;
1428 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
1429 	})));
1430 	expect_open(ino, 0, 1);
1431 	EXPECT_CALL(*m_mock, process(
1432 		ResultOf([=](auto in) {
1433 			return (in.header.opcode == FUSE_GETATTR &&
1434 				in.header.nodeid == ino);
1435 		}, Eq(true)),
1436 		_)
1437 	).WillRepeatedly(Invoke(
1438 	ReturnImmediate([=](auto i __unused, auto& out) {
1439 		SET_OUT_HEADER_LEN(out, attr);
1440 		out.body.attr.attr.ino = ino;
1441 		out.body.attr.attr.mode = mode;
1442 		out.body.attr.attr_valid = attr_valid;
1443 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
1444 		out.body.attr.attr.atime = server_time;
1445 		out.body.attr.attr.mtime = server_time;
1446 		out.body.attr.attr.ctime = server_time;
1447 	})));
1448 
1449 	fd = open(FULLPATH, O_RDWR);
1450 	ASSERT_LE(0, fd) << strerror(errno);
1451 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1452 
1453 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1454 	EXPECT_EQ((time_t)server_time, sb.st_atime);
1455 	EXPECT_NE((time_t)server_time, sb.st_mtime);
1456 	EXPECT_NE((time_t)server_time, sb.st_ctime);
1457 
1458 	leak(fd);
1459 }
1460 
1461 /* Any dirty timestamp fields should be flushed during a SETATTR */
1462 TEST_F(WriteBackAsync, timestamps_during_setattr)
1463 {
1464 	const char FULLPATH[] = "mountpoint/some_file.txt";
1465 	const char RELPATH[] = "some_file.txt";
1466 	const char *CONTENTS = "abcdefgh";
1467 	ssize_t bufsize = strlen(CONTENTS);
1468 	uint64_t ino = 42;
1469 	const mode_t newmode = 0755;
1470 	int fd;
1471 
1472 	expect_lookup(RELPATH, ino, 0);
1473 	expect_open(ino, 0, 1);
1474 	EXPECT_CALL(*m_mock, process(
1475 		ResultOf([=](auto in) {
1476 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1477 			return (in.header.opcode == FUSE_SETATTR &&
1478 				in.header.nodeid == ino &&
1479 				in.body.setattr.valid == valid);
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 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1491 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1492 
1493 	leak(fd);
1494 }
1495 
1496 /* fuse_init_out.time_gran controls the granularity of timestamps */
1497 TEST_P(TimeGran, timestamps_during_setattr)
1498 {
1499 	const char FULLPATH[] = "mountpoint/some_file.txt";
1500 	const char RELPATH[] = "some_file.txt";
1501 	const char *CONTENTS = "abcdefgh";
1502 	ssize_t bufsize = strlen(CONTENTS);
1503 	uint64_t ino = 42;
1504 	const mode_t newmode = 0755;
1505 	int fd;
1506 
1507 	expect_lookup(RELPATH, ino, 0);
1508 	expect_open(ino, 0, 1);
1509 	EXPECT_CALL(*m_mock, process(
1510 		ResultOf([=](auto in) {
1511 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1512 			return (in.header.opcode == FUSE_SETATTR &&
1513 				in.header.nodeid == ino &&
1514 				in.body.setattr.valid == valid &&
1515 				in.body.setattr.mtimensec % m_time_gran == 0 &&
1516 				in.body.setattr.ctimensec % m_time_gran == 0);
1517 		}, Eq(true)),
1518 		_)
1519 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1520 		SET_OUT_HEADER_LEN(out, attr);
1521 		out.body.attr.attr.ino = ino;
1522 		out.body.attr.attr.mode = S_IFREG | newmode;
1523 	})));
1524 
1525 	fd = open(FULLPATH, O_RDWR);
1526 	ASSERT_LE(0, fd) << strerror(errno);
1527 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1528 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1529 
1530 	leak(fd);
1531 }
1532 
1533 INSTANTIATE_TEST_SUITE_P(RA, TimeGran, Range(0u, 10u));
1534 
1535 /*
1536  * Without direct_io, writes should be committed to cache
1537  */
1538 TEST_F(Write, writethrough)
1539 {
1540 	const char FULLPATH[] = "mountpoint/some_file.txt";
1541 	const char RELPATH[] = "some_file.txt";
1542 	const char *CONTENTS = "abcdefgh";
1543 	uint64_t ino = 42;
1544 	int fd;
1545 	ssize_t bufsize = strlen(CONTENTS);
1546 	uint8_t readbuf[bufsize];
1547 
1548 	expect_lookup(RELPATH, ino, 0);
1549 	expect_open(ino, 0, 1);
1550 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
1551 
1552 	fd = open(FULLPATH, O_RDWR);
1553 	ASSERT_LE(0, fd) << strerror(errno);
1554 
1555 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1556 	/*
1557 	 * A subsequent read should be serviced by cache, without querying the
1558 	 * filesystem daemon
1559 	 */
1560 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1561 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1562 	leak(fd);
1563 }
1564 
1565 /* Writes that extend a file should update the cached file size */
1566 TEST_F(Write, update_file_size)
1567 {
1568 	const char FULLPATH[] = "mountpoint/some_file.txt";
1569 	const char RELPATH[] = "some_file.txt";
1570 	const char *CONTENTS = "abcdefgh";
1571 	struct stat sb;
1572 	uint64_t ino = 42;
1573 	int fd;
1574 	ssize_t bufsize = strlen(CONTENTS);
1575 
1576 	expect_lookup(RELPATH, ino, 0);
1577 	expect_open(ino, 0, 1);
1578 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
1579 
1580 	fd = open(FULLPATH, O_RDWR);
1581 	ASSERT_LE(0, fd) << strerror(errno);
1582 
1583 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1584 	/* Get cached attributes */
1585 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1586 	ASSERT_EQ(bufsize, sb.st_size);
1587 	leak(fd);
1588 }
1589