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