xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision b2792a300ddb8d8334b234fe7744f5141cc96103)
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:
SetUp()53 void SetUp() {
54 	FuseTest::SetUp();
55 }
56 
TearDown()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 
expect_lookup(const char * relpath,uint64_t ino,uint64_t size)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 
expect_release(uint64_t ino,ProcessMockerT r)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 
expect_write(uint64_t ino,uint64_t offset,uint64_t isize,uint64_t osize,const void * contents)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 */
maybe_expect_write(uint64_t ino,uint64_t offset,uint64_t size,const void * contents)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:
SetUp()121 virtual void SetUp() {
122 	m_kernel_minor_version = 8;
123 	FuseTest::SetUp();
124 }
125 
expect_lookup(const char * relpath,uint64_t ino,uint64_t size)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 {
SetUp()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:
SetUp()145 virtual void SetUp() {
146 	m_init_flags |= FUSE_WRITEBACK_CACHE;
147 	FuseTest::SetUp();
148 	if (IsSkipped())
149 		return;
150 }
151 
expect_write(uint64_t ino,uint64_t offset,uint64_t isize,uint64_t osize,const void * contents)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:
SetUp()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:
SetUp()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:
SetUp()180 virtual void SetUp() {
181 	m_async = true;
182 	m_maxwrite = UINT32_MAX; // 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 * (unsigned long )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:
SetUp()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 
SetUp()214 void SetUp() {
215 	s_sigxfsz = 0;
216 	getrlimit(RLIMIT_FSIZE, &m_initial_limit);
217 	FuseTest::SetUp();
218 }
219 
TearDown()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 
sigxfsz_handler(int __unused sig)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 */
TEST_F(AioWrite,DISABLED_aio_write)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  */
TEST_F(Write,append)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 */
TEST_F(Write,append_to_cached)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 
TEST_F(Write,append_direct_io)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 */
TEST_F(Write,direct_io_evicts_cache)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  */
TEST_F(Write,indirect_io_short_write)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 */
TEST_F(Write,indirect_io_long_write)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  */
TEST_F(Write,indirect_io_very_long_write)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  */
TEST_F(Write,direct_io_short_write)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  */
TEST_F(Write,direct_io_short_write_iov)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 */
TEST_P(WriteRlimitFsize,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  */
TEST_P(WriteRlimitFsize,rlimit_fsize_truncate)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  */
TEST_F(Write,eof_during_rmw)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  */
TEST_P(WriteEofDuringVnopStrategy,eof_during_vop_strategy)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 */
TEST_F(Write,mmap)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 
TEST_F(Write,pwrite)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 */
TEST_F(Write,timestamps)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 
TEST_F(Write,write)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 */
TEST_F(WriteMaxWrite,write)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 ||
864 	    (unsigned long )halfbufsize >= m_maxphys)
865 		GTEST_SKIP() << "Must lower m_maxwrite for this test";
866 	bufsize = halfbufsize * 2;
867 	contents = new int[bufsize / sizeof(int)];
868 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
869 		contents[i] = i;
870 	}
871 
872 	expect_lookup(RELPATH, ino, 0);
873 	expect_open(ino, 0, 1);
874 	maybe_expect_write(ino, 0, halfbufsize, contents);
875 	maybe_expect_write(ino, halfbufsize, halfbufsize,
876 		&contents[halfbufsize / sizeof(int)]);
877 
878 	fd = open(FULLPATH, O_WRONLY);
879 	ASSERT_LE(0, fd) << strerror(errno);
880 
881 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
882 	leak(fd);
883 
884 	delete[] contents;
885 }
886 
TEST_F(Write,write_nothing)887 TEST_F(Write, write_nothing)
888 {
889 	const char FULLPATH[] = "mountpoint/some_file.txt";
890 	const char RELPATH[] = "some_file.txt";
891 	const char *CONTENTS = "";
892 	uint64_t ino = 42;
893 	int fd;
894 	ssize_t bufsize = 0;
895 
896 	expect_lookup(RELPATH, ino, 0);
897 	expect_open(ino, 0, 1);
898 
899 	fd = open(FULLPATH, O_WRONLY);
900 	ASSERT_LE(0, fd) << strerror(errno);
901 
902 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
903 	leak(fd);
904 }
905 
TEST_F(Write_7_8,write)906 TEST_F(Write_7_8, write)
907 {
908 	const char FULLPATH[] = "mountpoint/some_file.txt";
909 	const char RELPATH[] = "some_file.txt";
910 	const char *CONTENTS = "abcdefgh";
911 	uint64_t ino = 42;
912 	int fd;
913 	ssize_t bufsize = strlen(CONTENTS);
914 
915 	expect_lookup(RELPATH, ino, 0);
916 	expect_open(ino, 0, 1);
917 	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
918 
919 	fd = open(FULLPATH, O_WRONLY);
920 	ASSERT_LE(0, fd) << strerror(errno);
921 
922 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
923 	leak(fd);
924 }
925 
926 /* In writeback mode, dirty data should be written on close */
TEST_F(WriteBackAsync,close)927 TEST_F(WriteBackAsync, close)
928 {
929 	const char FULLPATH[] = "mountpoint/some_file.txt";
930 	const char RELPATH[] = "some_file.txt";
931 	const char *CONTENTS = "abcdefgh";
932 	uint64_t ino = 42;
933 	int fd;
934 	ssize_t bufsize = strlen(CONTENTS);
935 
936 	expect_lookup(RELPATH, ino, 0);
937 	expect_open(ino, 0, 1);
938 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
939 	EXPECT_CALL(*m_mock, process(
940 		ResultOf([=](auto in) {
941 			return (in.header.opcode == FUSE_SETATTR);
942 		}, Eq(true)),
943 		_)
944 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
945 		SET_OUT_HEADER_LEN(out, attr);
946 		out.body.attr.attr.ino = ino;	// Must match nodeid
947 	})));
948 	expect_flush(ino, 1, ReturnErrno(0));
949 	expect_release(ino, ReturnErrno(0));
950 
951 	fd = open(FULLPATH, O_RDWR);
952 	ASSERT_LE(0, fd) << strerror(errno);
953 
954 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
955 	close(fd);
956 }
957 
958 /* In writeback mode, adjacent writes will be clustered together */
TEST_F(WriteCluster,clustering)959 TEST_F(WriteCluster, clustering)
960 {
961 	const char FULLPATH[] = "mountpoint/some_file.txt";
962 	const char RELPATH[] = "some_file.txt";
963 	uint64_t ino = 42;
964 	int i, fd;
965 	char *wbuf, *wbuf2x;
966 	ssize_t bufsize = m_maxbcachebuf;
967 	off_t filesize = 5 * bufsize;
968 
969 	wbuf = new char[bufsize];
970 	memset(wbuf, 'X', bufsize);
971 	wbuf2x = new char[2 * bufsize];
972 	memset(wbuf2x, 'X', 2 * bufsize);
973 
974 	expect_lookup(RELPATH, ino, filesize);
975 	expect_open(ino, 0, 1);
976 	/*
977 	 * Writes of bufsize-bytes each should be clustered into greater sizes.
978 	 * The amount of clustering is adaptive, so the first write actually
979 	 * issued will be 2x bufsize and subsequent writes may be larger
980 	 */
981 	expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
982 	expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
983 	expect_flush(ino, 1, ReturnErrno(0));
984 	expect_release(ino, ReturnErrno(0));
985 
986 	fd = open(FULLPATH, O_RDWR);
987 	ASSERT_LE(0, fd) << strerror(errno);
988 
989 	for (i = 0; i < 4; i++) {
990 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
991 			<< strerror(errno);
992 	}
993 	close(fd);
994 	delete[] wbuf2x;
995 	delete[] wbuf;
996 }
997 
998 /*
999  * When clustering writes, an I/O error to any of the cluster's children should
1000  * not panic the system on unmount
1001  */
1002 /*
1003  * Regression test for bug 238585
1004  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
1005  */
TEST_F(WriteCluster,cluster_write_err)1006 TEST_F(WriteCluster, cluster_write_err)
1007 {
1008 	const char FULLPATH[] = "mountpoint/some_file.txt";
1009 	const char RELPATH[] = "some_file.txt";
1010 	uint64_t ino = 42;
1011 	int i, fd;
1012 	char *wbuf;
1013 	ssize_t bufsize = m_maxbcachebuf;
1014 	off_t filesize = 4 * bufsize;
1015 
1016 	wbuf = new char[bufsize];
1017 	memset(wbuf, 'X', bufsize);
1018 
1019 	expect_lookup(RELPATH, ino, filesize);
1020 	expect_open(ino, 0, 1);
1021 	EXPECT_CALL(*m_mock, process(
1022 		ResultOf([=](auto in) {
1023 			return (in.header.opcode == FUSE_WRITE);
1024 		}, Eq(true)),
1025 		_)
1026 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
1027 	expect_flush(ino, 1, ReturnErrno(0));
1028 	expect_release(ino, ReturnErrno(0));
1029 
1030 	fd = open(FULLPATH, O_RDWR);
1031 	ASSERT_LE(0, fd) << strerror(errno);
1032 
1033 	for (i = 0; i < 3; i++) {
1034 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
1035 			<< strerror(errno);
1036 	}
1037 	close(fd);
1038 	delete[] wbuf;
1039 }
1040 
1041 /*
1042  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
1043  * server.  The FUSE protocol explicitly allows that.
1044  */
TEST_F(WriteBack,rmw)1045 TEST_F(WriteBack, rmw)
1046 {
1047 	const char FULLPATH[] = "mountpoint/some_file.txt";
1048 	const char RELPATH[] = "some_file.txt";
1049 	const char *CONTENTS = "abcdefgh";
1050 	const char *INITIAL   = "XXXXXXXXXX";
1051 	uint64_t ino = 42;
1052 	uint64_t offset = 1;
1053 	off_t fsize = 10;
1054 	int fd;
1055 	ssize_t bufsize = strlen(CONTENTS);
1056 
1057 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
1058 	expect_open(ino, 0, 1);
1059 	expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
1060 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
1061 
1062 	fd = open(FULLPATH, O_WRONLY);
1063 	ASSERT_LE(0, fd) << strerror(errno);
1064 
1065 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
1066 		<< strerror(errno);
1067 	leak(fd);
1068 }
1069 
1070 /*
1071  * Without direct_io, writes should be committed to cache
1072  */
TEST_F(WriteBack,cache)1073 TEST_F(WriteBack, cache)
1074 {
1075 	const char FULLPATH[] = "mountpoint/some_file.txt";
1076 	const char RELPATH[] = "some_file.txt";
1077 	const char *CONTENTS = "abcdefgh";
1078 	uint64_t ino = 42;
1079 	int fd;
1080 	ssize_t bufsize = strlen(CONTENTS);
1081 	uint8_t readbuf[bufsize];
1082 
1083 	expect_lookup(RELPATH, ino, 0);
1084 	expect_open(ino, 0, 1);
1085 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
1086 
1087 	fd = open(FULLPATH, O_RDWR);
1088 	ASSERT_LE(0, fd) << strerror(errno);
1089 
1090 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1091 	/*
1092 	 * A subsequent read should be serviced by cache, without querying the
1093 	 * filesystem daemon
1094 	 */
1095 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1096 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1097 	leak(fd);
1098 }
1099 
1100 /*
1101  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
1102  * an odd test, because it would be unusual to use O_DIRECT for writes but not
1103  * reads.
1104  */
TEST_F(WriteBack,o_direct)1105 TEST_F(WriteBack, o_direct)
1106 {
1107 	const char FULLPATH[] = "mountpoint/some_file.txt";
1108 	const char RELPATH[] = "some_file.txt";
1109 	const char *CONTENTS = "abcdefgh";
1110 	uint64_t ino = 42;
1111 	int fd;
1112 	ssize_t bufsize = strlen(CONTENTS);
1113 	uint8_t readbuf[bufsize];
1114 
1115 	expect_lookup(RELPATH, ino, 0);
1116 	expect_open(ino, 0, 1);
1117 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1118 		CONTENTS);
1119 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1120 
1121 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
1122 	ASSERT_LE(0, fd) << strerror(errno);
1123 
1124 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1125 	/* A subsequent read must query the daemon because cache is empty */
1126 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1127 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1128 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1129 	leak(fd);
1130 }
1131 
TEST_F(WriteBack,direct_io)1132 TEST_F(WriteBack, direct_io)
1133 {
1134 	const char FULLPATH[] = "mountpoint/some_file.txt";
1135 	const char RELPATH[] = "some_file.txt";
1136 	const char *CONTENTS = "abcdefgh";
1137 	uint64_t ino = 42;
1138 	int fd;
1139 	ssize_t bufsize = strlen(CONTENTS);
1140 	uint8_t readbuf[bufsize];
1141 
1142 	expect_lookup(RELPATH, ino, 0);
1143 	expect_open(ino, FOPEN_DIRECT_IO, 1);
1144 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1145 		CONTENTS);
1146 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1147 
1148 	fd = open(FULLPATH, O_RDWR);
1149 	ASSERT_LE(0, fd) << strerror(errno);
1150 
1151 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1152 	/* A subsequent read must query the daemon because cache is empty */
1153 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1154 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1155 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1156 	leak(fd);
1157 }
1158 
1159 /*
1160  * mmap should still be possible even if the server used direct_io.  Mmap will
1161  * still use the cache, though.
1162  *
1163  * Regression test for bug 247276
1164  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276
1165  */
TEST_F(WriteBack,mmap_direct_io)1166 TEST_F(WriteBack, mmap_direct_io)
1167 {
1168 	const char FULLPATH[] = "mountpoint/some_file.txt";
1169 	const char RELPATH[] = "some_file.txt";
1170 	const char *CONTENTS = "abcdefgh";
1171 	uint64_t ino = 42;
1172 	int fd;
1173 	size_t len;
1174 	ssize_t bufsize = strlen(CONTENTS);
1175 	char *zeros;
1176 	void *p;
1177 
1178 	len = getpagesize();
1179 	zeros = new char[len]();
1180 
1181 	expect_lookup(RELPATH, ino, len);
1182 	expect_open(ino, FOPEN_DIRECT_IO, 1);
1183 	expect_read(ino, 0, len, len, zeros);
1184 	expect_flush(ino, 1, ReturnErrno(0));
1185 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros);
1186 	expect_release(ino, ReturnErrno(0));
1187 
1188 	fd = open(FULLPATH, O_RDWR);
1189 	ASSERT_LE(0, fd) << strerror(errno);
1190 
1191 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1192 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1193 
1194 	memmove((uint8_t*)p, CONTENTS, bufsize);
1195 
1196 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
1197 	close(fd);	// Write mmap'd data on close
1198 
1199 	delete[] zeros;
1200 }
1201 
1202 /*
1203  * When mounted with -o async, the writeback cache mode should delay writes
1204  */
TEST_F(WriteBackAsync,delay)1205 TEST_F(WriteBackAsync, delay)
1206 {
1207 	const char FULLPATH[] = "mountpoint/some_file.txt";
1208 	const char RELPATH[] = "some_file.txt";
1209 	const char *CONTENTS = "abcdefgh";
1210 	uint64_t ino = 42;
1211 	int fd;
1212 	ssize_t bufsize = strlen(CONTENTS);
1213 
1214 	expect_lookup(RELPATH, ino, 0);
1215 	expect_open(ino, 0, 1);
1216 	/* Write should be cached, but FUSE_WRITE shouldn't be sent */
1217 	EXPECT_CALL(*m_mock, process(
1218 		ResultOf([=](auto in) {
1219 			return (in.header.opcode == FUSE_WRITE);
1220 		}, Eq(true)),
1221 		_)
1222 	).Times(0);
1223 
1224 	fd = open(FULLPATH, O_RDWR);
1225 	ASSERT_LE(0, fd) << strerror(errno);
1226 
1227 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1228 
1229 	/* Don't close the file because that would flush the cache */
1230 	leak(fd);
1231 }
1232 
1233 /*
1234  * A direct write should not evict dirty cached data from outside of its own
1235  * byte range.
1236  */
TEST_F(WriteBackAsync,direct_io_ignores_unrelated_cached)1237 TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached)
1238 {
1239 	const char FULLPATH[] = "mountpoint/some_file.txt";
1240 	const char RELPATH[] = "some_file.txt";
1241 	const char CONTENTS0[] = "abcdefgh";
1242 	const char CONTENTS1[] = "ijklmnop";
1243 	uint64_t ino = 42;
1244 	int fd;
1245 	ssize_t bufsize = strlen(CONTENTS0) + 1;
1246 	ssize_t fsize = 2 * m_maxbcachebuf;
1247 	char readbuf[bufsize];
1248 	char *zeros;
1249 
1250 	zeros = new char[m_maxbcachebuf]();
1251 
1252 	expect_lookup(RELPATH, ino, fsize);
1253 	expect_open(ino, 0, 1);
1254 	expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros);
1255 	FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0,
1256 		CONTENTS1);
1257 
1258 	fd = open(FULLPATH, O_RDWR);
1259 	ASSERT_LE(0, fd) << strerror(errno);
1260 
1261 	// Cache first block with dirty data.  This will entail first reading
1262 	// the existing data.
1263 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0))
1264 		<< strerror(errno);
1265 
1266 	// Write directly to second block
1267 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1268 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf))
1269 		<< strerror(errno);
1270 
1271 	// Read from the first block again.  Should be serviced by cache.
1272 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1273 	ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno);
1274 	ASSERT_STREQ(readbuf, CONTENTS0);
1275 
1276 	leak(fd);
1277 	delete[] zeros;
1278 }
1279 
1280 /*
1281  * If a direct io write partially overlaps one or two blocks of dirty cached
1282  * data, No dirty data should be lost.  Admittedly this is a weird test,
1283  * because it would be unusual to use O_DIRECT and the writeback cache.
1284  */
TEST_F(WriteBackAsync,direct_io_partially_overlaps_cached_block)1285 TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block)
1286 {
1287 	const char FULLPATH[] = "mountpoint/some_file.txt";
1288 	const char RELPATH[] = "some_file.txt";
1289 	uint64_t ino = 42;
1290 	int fd;
1291 	off_t bs = m_maxbcachebuf;
1292 	ssize_t fsize = 3 * bs;
1293 	char *readbuf, *zeros, *ones, *zeroones, *onezeros;
1294 
1295 	readbuf = new char[bs];
1296 	zeros = new char[3 * bs]();
1297 	ones = new char[2 * bs];
1298 	memset(ones, 1, 2 * bs);
1299 	zeroones = new char[bs]();
1300 	memset((uint8_t*)zeroones + bs / 2, 1, bs / 2);
1301 	onezeros = new char[bs]();
1302 	memset(onezeros, 1, bs / 2);
1303 
1304 	expect_lookup(RELPATH, ino, fsize);
1305 	expect_open(ino, 0, 1);
1306 
1307 	fd = open(FULLPATH, O_RDWR);
1308 	ASSERT_LE(0, fd) << strerror(errno);
1309 
1310 	/* Cache first and third blocks with dirty data.  */
1311 	ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno);
1312 
1313 	/*
1314 	 * Write directly to all three blocks.  The partially written blocks
1315 	 * will be flushed because they're dirty.
1316 	 */
1317 	FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros);
1318 	FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros);
1319 	/* The direct write is split in two because of the m_maxwrite value */
1320 	FuseTest::expect_write(ino,     bs / 2, bs, bs, 0, 0, ones);
1321 	FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones);
1322 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1323 	ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno);
1324 
1325 	/*
1326 	 * Read from both the valid and invalid portions of the first and third
1327 	 * blocks again.  This will entail FUSE_READ operations because these
1328 	 * blocks were invalidated by the direct write.
1329 	 */
1330 	expect_read(ino, 0, bs, bs, zeroones);
1331 	expect_read(ino, 2 * bs, bs, bs, onezeros);
1332 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1333 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno);
1334 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1335 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2))
1336 		<< strerror(errno);
1337 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1338 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2))
1339 		<< strerror(errno);
1340 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1341 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs))
1342 		<< strerror(errno);
1343 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1344 
1345 	leak(fd);
1346 	delete[] zeroones;
1347 	delete[] onezeros;
1348 	delete[] ones;
1349 	delete[] zeros;
1350 	delete[] readbuf;
1351 }
1352 
1353 /*
1354  * In WriteBack mode, writes may be cached beyond what the server thinks is the
1355  * EOF.  In this case, a short read at EOF should _not_ cause fusefs to update
1356  * the file's size.
1357  */
TEST_F(WriteBackAsync,eof)1358 TEST_F(WriteBackAsync, eof)
1359 {
1360 	const char FULLPATH[] = "mountpoint/some_file.txt";
1361 	const char RELPATH[] = "some_file.txt";
1362 	const char *CONTENTS0 = "abcdefgh";
1363 	const char *CONTENTS1 = "ijklmnop";
1364 	uint64_t ino = 42;
1365 	int fd;
1366 	off_t offset = m_maxbcachebuf;
1367 	ssize_t wbufsize = strlen(CONTENTS1);
1368 	off_t old_filesize = (off_t)strlen(CONTENTS0);
1369 	ssize_t rbufsize = 2 * old_filesize;
1370 	char readbuf[rbufsize];
1371 	size_t holesize = rbufsize - old_filesize;
1372 	char hole[holesize];
1373 	struct stat sb;
1374 	ssize_t r;
1375 
1376 	expect_lookup(RELPATH, ino, 0);
1377 	expect_open(ino, 0, 1);
1378 	expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
1379 
1380 	fd = open(FULLPATH, O_RDWR);
1381 	ASSERT_LE(0, fd) << strerror(errno);
1382 
1383 	/* Write and cache data beyond EOF */
1384 	ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
1385 		<< strerror(errno);
1386 
1387 	/* Read from the old EOF */
1388 	r = pread(fd, readbuf, rbufsize, 0);
1389 	ASSERT_LE(0, r) << strerror(errno);
1390 	EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
1391 	EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
1392 	bzero(hole, holesize);
1393 	EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
1394 
1395 	/* The file's size should still be what was established by pwrite */
1396 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1397 	EXPECT_EQ(offset + wbufsize, sb.st_size);
1398 	leak(fd);
1399 }
1400 
1401 /*
1402  * When a file has dirty writes that haven't been flushed, the server's notion
1403  * of its mtime and ctime will be wrong.  The kernel should ignore those if it
1404  * gets them from a FUSE_GETATTR before flushing.
1405  */
TEST_F(WriteBackAsync,timestamps)1406 TEST_F(WriteBackAsync, timestamps)
1407 {
1408 	const char FULLPATH[] = "mountpoint/some_file.txt";
1409 	const char RELPATH[] = "some_file.txt";
1410 	const char *CONTENTS = "abcdefgh";
1411 	ssize_t bufsize = strlen(CONTENTS);
1412 	uint64_t ino = 42;
1413 	uint64_t attr_valid = 0;
1414 	uint64_t attr_valid_nsec = 0;
1415 	uint64_t server_time = 12345;
1416 	mode_t mode = S_IFREG | 0644;
1417 	int fd;
1418 
1419 	struct stat sb;
1420 
1421 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
1422 	.WillRepeatedly(Invoke(
1423 		ReturnImmediate([=](auto in __unused, auto& out) {
1424 		SET_OUT_HEADER_LEN(out, entry);
1425 		out.body.entry.attr.mode = mode;
1426 		out.body.entry.nodeid = ino;
1427 		out.body.entry.attr.nlink = 1;
1428 		out.body.entry.attr_valid = attr_valid;
1429 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
1430 	})));
1431 	expect_open(ino, 0, 1);
1432 	EXPECT_CALL(*m_mock, process(
1433 		ResultOf([=](auto in) {
1434 			return (in.header.opcode == FUSE_GETATTR &&
1435 				in.header.nodeid == ino);
1436 		}, Eq(true)),
1437 		_)
1438 	).WillRepeatedly(Invoke(
1439 	ReturnImmediate([=](auto i __unused, auto& out) {
1440 		SET_OUT_HEADER_LEN(out, attr);
1441 		out.body.attr.attr.ino = ino;
1442 		out.body.attr.attr.mode = mode;
1443 		out.body.attr.attr_valid = attr_valid;
1444 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
1445 		out.body.attr.attr.atime = server_time;
1446 		out.body.attr.attr.mtime = server_time;
1447 		out.body.attr.attr.ctime = server_time;
1448 	})));
1449 
1450 	fd = open(FULLPATH, O_RDWR);
1451 	ASSERT_LE(0, fd) << strerror(errno);
1452 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1453 
1454 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1455 	EXPECT_EQ((time_t)server_time, sb.st_atime);
1456 	EXPECT_NE((time_t)server_time, sb.st_mtime);
1457 	EXPECT_NE((time_t)server_time, sb.st_ctime);
1458 
1459 	leak(fd);
1460 }
1461 
1462 /* Any dirty timestamp fields should be flushed during a SETATTR */
TEST_F(WriteBackAsync,timestamps_during_setattr)1463 TEST_F(WriteBackAsync, timestamps_during_setattr)
1464 {
1465 	const char FULLPATH[] = "mountpoint/some_file.txt";
1466 	const char RELPATH[] = "some_file.txt";
1467 	const char *CONTENTS = "abcdefgh";
1468 	ssize_t bufsize = strlen(CONTENTS);
1469 	uint64_t ino = 42;
1470 	const mode_t newmode = 0755;
1471 	int fd;
1472 
1473 	expect_lookup(RELPATH, ino, 0);
1474 	expect_open(ino, 0, 1);
1475 	EXPECT_CALL(*m_mock, process(
1476 		ResultOf([=](auto in) {
1477 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1478 			return (in.header.opcode == FUSE_SETATTR &&
1479 				in.header.nodeid == ino &&
1480 				in.body.setattr.valid == valid);
1481 		}, Eq(true)),
1482 		_)
1483 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1484 		SET_OUT_HEADER_LEN(out, attr);
1485 		out.body.attr.attr.ino = ino;
1486 		out.body.attr.attr.mode = S_IFREG | newmode;
1487 	})));
1488 
1489 	fd = open(FULLPATH, O_RDWR);
1490 	ASSERT_LE(0, fd) << strerror(errno);
1491 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1492 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1493 
1494 	leak(fd);
1495 }
1496 
1497 /* fuse_init_out.time_gran controls the granularity of timestamps */
TEST_P(TimeGran,timestamps_during_setattr)1498 TEST_P(TimeGran, timestamps_during_setattr)
1499 {
1500 	const char FULLPATH[] = "mountpoint/some_file.txt";
1501 	const char RELPATH[] = "some_file.txt";
1502 	const char *CONTENTS = "abcdefgh";
1503 	ssize_t bufsize = strlen(CONTENTS);
1504 	uint64_t ino = 42;
1505 	const mode_t newmode = 0755;
1506 	int fd;
1507 
1508 	expect_lookup(RELPATH, ino, 0);
1509 	expect_open(ino, 0, 1);
1510 	EXPECT_CALL(*m_mock, process(
1511 		ResultOf([=](auto in) {
1512 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1513 			return (in.header.opcode == FUSE_SETATTR &&
1514 				in.header.nodeid == ino &&
1515 				in.body.setattr.valid == valid &&
1516 				in.body.setattr.mtimensec % m_time_gran == 0 &&
1517 				in.body.setattr.ctimensec % m_time_gran == 0);
1518 		}, Eq(true)),
1519 		_)
1520 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1521 		SET_OUT_HEADER_LEN(out, attr);
1522 		out.body.attr.attr.ino = ino;
1523 		out.body.attr.attr.mode = S_IFREG | newmode;
1524 	})));
1525 
1526 	fd = open(FULLPATH, O_RDWR);
1527 	ASSERT_LE(0, fd) << strerror(errno);
1528 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1529 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1530 
1531 	leak(fd);
1532 }
1533 
1534 INSTANTIATE_TEST_SUITE_P(RA, TimeGran, Range(0u, 10u));
1535 
1536 /*
1537  * Without direct_io, writes should be committed to cache
1538  */
TEST_F(Write,writethrough)1539 TEST_F(Write, writethrough)
1540 {
1541 	const char FULLPATH[] = "mountpoint/some_file.txt";
1542 	const char RELPATH[] = "some_file.txt";
1543 	const char *CONTENTS = "abcdefgh";
1544 	uint64_t ino = 42;
1545 	int fd;
1546 	ssize_t bufsize = strlen(CONTENTS);
1547 	uint8_t readbuf[bufsize];
1548 
1549 	expect_lookup(RELPATH, ino, 0);
1550 	expect_open(ino, 0, 1);
1551 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
1552 
1553 	fd = open(FULLPATH, O_RDWR);
1554 	ASSERT_LE(0, fd) << strerror(errno);
1555 
1556 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1557 	/*
1558 	 * A subsequent read should be serviced by cache, without querying the
1559 	 * filesystem daemon
1560 	 */
1561 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1562 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1563 	leak(fd);
1564 }
1565 
1566 /* Writes that extend a file should update the cached file size */
TEST_F(Write,update_file_size)1567 TEST_F(Write, update_file_size)
1568 {
1569 	const char FULLPATH[] = "mountpoint/some_file.txt";
1570 	const char RELPATH[] = "some_file.txt";
1571 	const char *CONTENTS = "abcdefgh";
1572 	struct stat sb;
1573 	uint64_t ino = 42;
1574 	int fd;
1575 	ssize_t bufsize = strlen(CONTENTS);
1576 
1577 	expect_lookup(RELPATH, ino, 0);
1578 	expect_open(ino, 0, 1);
1579 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
1580 
1581 	fd = open(FULLPATH, O_RDWR);
1582 	ASSERT_LE(0, fd) << strerror(errno);
1583 
1584 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1585 	/* Get cached attributes */
1586 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1587 	ASSERT_EQ(bufsize, sb.st_size);
1588 	leak(fd);
1589 }
1590