xref: /freebsd/tests/sys/fs/fusefs/fallocate.cc (revision 39f5d8dd)
1398c88c7SAlan Somers /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3398c88c7SAlan Somers  *
4398c88c7SAlan Somers  * Copyright (c) 2021 Alan Somers
5398c88c7SAlan Somers  *
6398c88c7SAlan Somers  * Redistribution and use in source and binary forms, with or without
7398c88c7SAlan Somers  * modification, are permitted provided that the following conditions
8398c88c7SAlan Somers  * are met:
9398c88c7SAlan Somers  * 1. Redistributions of source code must retain the above copyright
10398c88c7SAlan Somers  *    notice, this list of conditions and the following disclaimer.
11398c88c7SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
12398c88c7SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
13398c88c7SAlan Somers  *    documentation and/or other materials provided with the distribution.
14398c88c7SAlan Somers  *
15398c88c7SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16398c88c7SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17398c88c7SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18398c88c7SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19398c88c7SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20398c88c7SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21398c88c7SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22398c88c7SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23398c88c7SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24398c88c7SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25398c88c7SAlan Somers  * SUCH DAMAGE.
26398c88c7SAlan Somers  */
27398c88c7SAlan Somers 
28398c88c7SAlan Somers extern "C" {
29398c88c7SAlan Somers #include <sys/param.h>
30398c88c7SAlan Somers #include <sys/mount.h>
31398c88c7SAlan Somers #include <sys/resource.h>
32398c88c7SAlan Somers #include <sys/time.h>
33398c88c7SAlan Somers 
34398c88c7SAlan Somers #include <fcntl.h>
35398c88c7SAlan Somers #include <signal.h>
36398c88c7SAlan Somers #include <unistd.h>
37398c88c7SAlan Somers 
38398c88c7SAlan Somers #include "mntopts.h"	// for build_iovec
39398c88c7SAlan Somers }
40398c88c7SAlan Somers 
41398c88c7SAlan Somers #include "mockfs.hh"
42398c88c7SAlan Somers #include "utils.hh"
43398c88c7SAlan Somers 
44398c88c7SAlan Somers using namespace testing;
45398c88c7SAlan Somers 
4689d57b94SAlan Somers /* Is buf all zero? */
4789d57b94SAlan Somers static bool
is_zero(const char * buf,uint64_t size)4889d57b94SAlan Somers is_zero(const char *buf, uint64_t size)
4989d57b94SAlan Somers {
5089d57b94SAlan Somers     return buf[0] == 0 && !memcmp(buf, buf + 1, size - 1);
5189d57b94SAlan Somers }
5289d57b94SAlan Somers 
5389d57b94SAlan Somers class Fallocate: public FuseTest {
5489d57b94SAlan Somers public:
5589d57b94SAlan Somers /*
5689d57b94SAlan Somers  * expect VOP_DEALLOCATE to be implemented by vop_stddeallocate.
5789d57b94SAlan Somers  */
expect_vop_stddeallocate(uint64_t ino,uint64_t off,uint64_t length)5889d57b94SAlan Somers void expect_vop_stddeallocate(uint64_t ino, uint64_t off, uint64_t length)
5989d57b94SAlan Somers {
6089d57b94SAlan Somers 	/* XXX read offset and size may depend on cache mode */
6189d57b94SAlan Somers 	EXPECT_CALL(*m_mock, process(
6289d57b94SAlan Somers 		ResultOf([=](auto in) {
6389d57b94SAlan Somers 			return (in.header.opcode == FUSE_READ &&
6489d57b94SAlan Somers 				in.header.nodeid == ino &&
6589d57b94SAlan Somers 				in.body.read.offset <= off &&
6689d57b94SAlan Somers 				in.body.read.offset + in.body.read.size >=
6789d57b94SAlan Somers 					off + length);
6889d57b94SAlan Somers 		}, Eq(true)),
6989d57b94SAlan Somers 		_)
7089d57b94SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
710c9df4afSAlan Somers 		assert(in.body.read.size <= sizeof(out.body.bytes));
7289d57b94SAlan Somers 		out.header.len = sizeof(struct fuse_out_header) +
7389d57b94SAlan Somers 			in.body.read.size;
7489d57b94SAlan Somers 		memset(out.body.bytes, 'X', in.body.read.size);
7589d57b94SAlan Somers 	}))).RetiresOnSaturation();
7689d57b94SAlan Somers 	EXPECT_CALL(*m_mock, process(
7789d57b94SAlan Somers 		ResultOf([=](auto in) {
7889d57b94SAlan Somers 			const char *buf = (const char*)in.body.bytes +
7989d57b94SAlan Somers 				sizeof(struct fuse_write_in);
8089d57b94SAlan Somers 
810c9df4afSAlan Somers 			assert(length <= sizeof(in.body.bytes) -
820c9df4afSAlan Somers 				sizeof(struct fuse_write_in));
8389d57b94SAlan Somers 			return (in.header.opcode == FUSE_WRITE &&
8489d57b94SAlan Somers 				in.header.nodeid == ino &&
8589d57b94SAlan Somers 				in.body.write.offset == off  &&
8689d57b94SAlan Somers 				in.body.write.size == length &&
8789d57b94SAlan Somers 				is_zero(buf, length));
8889d57b94SAlan Somers 		}, Eq(true)),
8989d57b94SAlan Somers 		_)
9089d57b94SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
9189d57b94SAlan Somers 		SET_OUT_HEADER_LEN(out, write);
9289d57b94SAlan Somers 		out.body.write.size = length;
9389d57b94SAlan Somers 	})));
9489d57b94SAlan Somers }
9589d57b94SAlan Somers };
9689d57b94SAlan Somers 
9789d57b94SAlan Somers class Fspacectl: public Fallocate {};
9889d57b94SAlan Somers 
9989d57b94SAlan Somers class Fspacectl_7_18: public Fspacectl {
10089d57b94SAlan Somers public:
SetUp()10189d57b94SAlan Somers virtual void SetUp() {
10289d57b94SAlan Somers 	m_kernel_minor_version = 18;
10389d57b94SAlan Somers 	Fspacectl::SetUp();
10489d57b94SAlan Somers }
10589d57b94SAlan Somers };
10689d57b94SAlan Somers 
10789d57b94SAlan Somers class FspacectlCache: public Fspacectl, public WithParamInterface<cache_mode> {
10889d57b94SAlan Somers public:
10989d57b94SAlan Somers bool m_direct_io;
11089d57b94SAlan Somers 
FspacectlCache()11189d57b94SAlan Somers FspacectlCache(): m_direct_io(false) {};
11289d57b94SAlan Somers 
SetUp()11389d57b94SAlan Somers virtual void SetUp() {
11489d57b94SAlan Somers 	int cache_mode = GetParam();
11589d57b94SAlan Somers 	switch (cache_mode) {
11689d57b94SAlan Somers 		case Uncached:
11789d57b94SAlan Somers 			m_direct_io = true;
11889d57b94SAlan Somers 			break;
11989d57b94SAlan Somers 		case WritebackAsync:
12089d57b94SAlan Somers 			m_async = true;
12189d57b94SAlan Somers 			/* FALLTHROUGH */
12289d57b94SAlan Somers 		case Writeback:
12389d57b94SAlan Somers 			m_init_flags |= FUSE_WRITEBACK_CACHE;
12489d57b94SAlan Somers 			/* FALLTHROUGH */
12589d57b94SAlan Somers 		case Writethrough:
12689d57b94SAlan Somers 			break;
12789d57b94SAlan Somers 		default:
12889d57b94SAlan Somers 			FAIL() << "Unknown cache mode";
12989d57b94SAlan Somers 	}
13089d57b94SAlan Somers 
13189d57b94SAlan Somers 	FuseTest::SetUp();
13289d57b94SAlan Somers 	if (IsSkipped())
13389d57b94SAlan Somers 		return;
13489d57b94SAlan Somers }
13589d57b94SAlan Somers };
136398c88c7SAlan Somers 
137398c88c7SAlan Somers class PosixFallocate: public Fallocate {
138398c88c7SAlan Somers public:
139398c88c7SAlan Somers static sig_atomic_t s_sigxfsz;
140398c88c7SAlan Somers 
SetUp()141398c88c7SAlan Somers void SetUp() {
142398c88c7SAlan Somers 	s_sigxfsz = 0;
143398c88c7SAlan Somers 	FuseTest::SetUp();
144398c88c7SAlan Somers }
145398c88c7SAlan Somers 
TearDown()146398c88c7SAlan Somers void TearDown() {
147398c88c7SAlan Somers 	struct sigaction sa;
148398c88c7SAlan Somers 
149398c88c7SAlan Somers 	bzero(&sa, sizeof(sa));
150398c88c7SAlan Somers 	sa.sa_handler = SIG_DFL;
151398c88c7SAlan Somers 	sigaction(SIGXFSZ, &sa, NULL);
152398c88c7SAlan Somers 
153398c88c7SAlan Somers 	Fallocate::TearDown();
154398c88c7SAlan Somers }
155398c88c7SAlan Somers 
156398c88c7SAlan Somers };
157398c88c7SAlan Somers 
158398c88c7SAlan Somers sig_atomic_t PosixFallocate::s_sigxfsz = 0;
159398c88c7SAlan Somers 
sigxfsz_handler(int __unused sig)160398c88c7SAlan Somers void sigxfsz_handler(int __unused sig) {
161398c88c7SAlan Somers 	PosixFallocate::s_sigxfsz = 1;
162398c88c7SAlan Somers }
163398c88c7SAlan Somers 
164398c88c7SAlan Somers class PosixFallocate_7_18: public PosixFallocate {
165398c88c7SAlan Somers public:
SetUp()166398c88c7SAlan Somers virtual void SetUp() {
167398c88c7SAlan Somers 	m_kernel_minor_version = 18;
168398c88c7SAlan Somers 	PosixFallocate::SetUp();
169398c88c7SAlan Somers }
170398c88c7SAlan Somers };
171398c88c7SAlan Somers 
172398c88c7SAlan Somers 
173398c88c7SAlan Somers /*
174398c88c7SAlan Somers  * If the server returns ENOSYS, it indicates that the server does not support
17589d57b94SAlan Somers  * FUSE_FALLOCATE.  This and future calls should fall back to vop_stddeallocate.
17689d57b94SAlan Somers  */
TEST_F(Fspacectl,enosys)17789d57b94SAlan Somers TEST_F(Fspacectl, enosys)
17889d57b94SAlan Somers {
17989d57b94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
18089d57b94SAlan Somers 	const char RELPATH[] = "some_file.txt";
18189d57b94SAlan Somers 	off_t fsize = 1 << 20;
18289d57b94SAlan Somers 	off_t off0 = 100;
18389d57b94SAlan Somers 	off_t len0 = 500;
18489d57b94SAlan Somers 	struct spacectl_range rqsr = { .r_offset = off0, .r_len = len0 };
18589d57b94SAlan Somers 	uint64_t ino = 42;
18689d57b94SAlan Somers 	uint64_t off1 = fsize;
18789d57b94SAlan Somers 	uint64_t len1 = 1000;
18889d57b94SAlan Somers 	off_t off2 = fsize / 2;
18989d57b94SAlan Somers 	off_t len2 = 500;
19089d57b94SAlan Somers 	int fd;
19189d57b94SAlan Somers 
19289d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
19389d57b94SAlan Somers 	expect_open(ino, 0, 1);
19489d57b94SAlan Somers 	expect_fallocate(ino, off0, len0,
19589d57b94SAlan Somers 		FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, ENOSYS);
19689d57b94SAlan Somers 	expect_vop_stddeallocate(ino, off0, len0);
19789d57b94SAlan Somers 	expect_vop_stddeallocate(ino, off2, len2);
19889d57b94SAlan Somers 
19989d57b94SAlan Somers 	fd = open(FULLPATH, O_RDWR);
20089d57b94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
20189d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
20289d57b94SAlan Somers 
20389d57b94SAlan Somers 	/* Subsequent calls shouldn't query the daemon either */
20489d57b94SAlan Somers 	rqsr.r_offset = off2;
20589d57b94SAlan Somers 	rqsr.r_len = len2;
20689d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
20789d57b94SAlan Somers 
20889d57b94SAlan Somers 	/* Neither should posix_fallocate query the daemon */
20989d57b94SAlan Somers 	EXPECT_EQ(EINVAL, posix_fallocate(fd, off1, len1));
21089d57b94SAlan Somers 
21189d57b94SAlan Somers 	leak(fd);
21289d57b94SAlan Somers }
21389d57b94SAlan Somers 
21489d57b94SAlan Somers /*
21589d57b94SAlan Somers  * EOPNOTSUPP means "the file system does not support fallocate with the
21689d57b94SAlan Somers  * supplied mode on this particular file".  So we should fallback, but not
21789d57b94SAlan Somers  * assume anything about whether the operation will fail on a different file or
21889d57b94SAlan Somers  * with a different mode.
21989d57b94SAlan Somers  */
TEST_F(Fspacectl,eopnotsupp)22089d57b94SAlan Somers TEST_F(Fspacectl, eopnotsupp)
22189d57b94SAlan Somers {
22289d57b94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
22389d57b94SAlan Somers 	const char RELPATH[] = "some_file.txt";
22489d57b94SAlan Somers 	struct spacectl_range rqsr;
22589d57b94SAlan Somers 	uint64_t ino = 42;
22689d57b94SAlan Somers 	uint64_t fsize = 1 << 20;
22789d57b94SAlan Somers 	uint64_t off0 = 500;
22889d57b94SAlan Somers 	uint64_t len = 1000;
22989d57b94SAlan Somers 	uint64_t off1 = fsize / 2;
23089d57b94SAlan Somers 	int fd;
23189d57b94SAlan Somers 
23289d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
23389d57b94SAlan Somers 	expect_open(ino, 0, 1);
23489d57b94SAlan Somers 	expect_fallocate(ino, off0, len,
23589d57b94SAlan Somers 		FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE,
23689d57b94SAlan Somers 	                EOPNOTSUPP);
23789d57b94SAlan Somers 	expect_vop_stddeallocate(ino, off0, len);
23889d57b94SAlan Somers 	expect_fallocate(ino, off1, len,
23989d57b94SAlan Somers 		FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE,
24089d57b94SAlan Somers 	                EOPNOTSUPP);
24189d57b94SAlan Somers 	expect_vop_stddeallocate(ino, off1, len);
24289d57b94SAlan Somers 	expect_fallocate(ino, fsize, len, 0, 0);
24389d57b94SAlan Somers 
24489d57b94SAlan Somers 	fd = open(FULLPATH, O_RDWR);
24589d57b94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
24689d57b94SAlan Somers 
24789d57b94SAlan Somers 	/*
24889d57b94SAlan Somers 	 * Though the FUSE daemon will reject the call, the kernel should fall
24989d57b94SAlan Somers 	 * back to a read-modify-write approach.
25089d57b94SAlan Somers 	 */
25189d57b94SAlan Somers 	rqsr.r_offset = off0;
25289d57b94SAlan Somers 	rqsr.r_len = len;
25389d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
25489d57b94SAlan Somers 
25589d57b94SAlan Somers 	/* Subsequent calls should still query the daemon */
25689d57b94SAlan Somers 	rqsr.r_offset = off1;
25789d57b94SAlan Somers 	rqsr.r_len = len;
25889d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
25989d57b94SAlan Somers 
26089d57b94SAlan Somers 	/* But subsequent posix_fallocate calls _should_ query the daemon */
26189d57b94SAlan Somers 	EXPECT_EQ(0, posix_fallocate(fd, fsize, len));
26289d57b94SAlan Somers 
26389d57b94SAlan Somers 	leak(fd);
26489d57b94SAlan Somers }
26589d57b94SAlan Somers 
TEST_F(Fspacectl,erofs)26689d57b94SAlan Somers TEST_F(Fspacectl, erofs)
26789d57b94SAlan Somers {
26889d57b94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
26989d57b94SAlan Somers 	const char RELPATH[] = "some_file.txt";
27089d57b94SAlan Somers 	struct statfs statbuf;
27189d57b94SAlan Somers 	uint64_t fsize = 2000;
27289d57b94SAlan Somers 	struct spacectl_range rqsr = { .r_offset = 0, .r_len = 1 };
27389d57b94SAlan Somers 	struct iovec *iov = NULL;
27489d57b94SAlan Somers 	int iovlen = 0;
27589d57b94SAlan Somers 	uint64_t ino = 42;
27689d57b94SAlan Somers 	int fd;
27789d57b94SAlan Somers 	int newflags;
27889d57b94SAlan Somers 
27989d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
28089d57b94SAlan Somers 	expect_open(ino, 0, 1);
28189d57b94SAlan Somers 	EXPECT_CALL(*m_mock, process(
28289d57b94SAlan Somers 		ResultOf([](auto in) {
28389d57b94SAlan Somers 			return (in.header.opcode == FUSE_STATFS);
28489d57b94SAlan Somers 		}, Eq(true)),
28589d57b94SAlan Somers 		_)
28689d57b94SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out)
28789d57b94SAlan Somers 	{
28889d57b94SAlan Somers 		/*
28989d57b94SAlan Somers 		 * All of the fields except f_flags are don't care, and f_flags
29089d57b94SAlan Somers 		 * is set by the VFS
29189d57b94SAlan Somers 		 */
29289d57b94SAlan Somers 		SET_OUT_HEADER_LEN(out, statfs);
29389d57b94SAlan Somers 	})));
29489d57b94SAlan Somers 
29589d57b94SAlan Somers 	fd = open(FULLPATH, O_RDWR);
29689d57b94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
29789d57b94SAlan Somers 
29889d57b94SAlan Somers 	/* Remount read-only */
29989d57b94SAlan Somers 	ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
30089d57b94SAlan Somers 	newflags = statbuf.f_flags | MNT_UPDATE | MNT_RDONLY;
30189d57b94SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", (void*)statbuf.f_fstypename, -1);
30289d57b94SAlan Somers 	build_iovec(&iov, &iovlen, "fspath", (void*)statbuf.f_mntonname, -1);
30389d57b94SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
30489d57b94SAlan Somers 	ASSERT_EQ(0, nmount(iov, iovlen, newflags)) << strerror(errno);
30539f5d8ddSAlan Somers 	free_iovec(&iov, &iovlen);
30689d57b94SAlan Somers 
30789d57b94SAlan Somers 	EXPECT_EQ(-1, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
30889d57b94SAlan Somers 	EXPECT_EQ(EROFS, errno);
30989d57b94SAlan Somers 
31089d57b94SAlan Somers 	leak(fd);
31189d57b94SAlan Somers }
31289d57b94SAlan Somers 
TEST_F(Fspacectl,ok)31389d57b94SAlan Somers TEST_F(Fspacectl, ok)
31489d57b94SAlan Somers {
31589d57b94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
31689d57b94SAlan Somers 	const char RELPATH[] = "some_file.txt";
31789d57b94SAlan Somers 	struct spacectl_range rqsr, rmsr;
31889d57b94SAlan Somers 	struct stat sb0, sb1;
31989d57b94SAlan Somers 	uint64_t ino = 42;
32089d57b94SAlan Somers 	uint64_t fsize = 2000;
32189d57b94SAlan Somers 	uint64_t offset = 500;
32289d57b94SAlan Somers 	uint64_t length = 1000;
32389d57b94SAlan Somers 	int fd;
32489d57b94SAlan Somers 
32589d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
32689d57b94SAlan Somers 	expect_open(ino, 0, 1);
32789d57b94SAlan Somers 	expect_fallocate(ino, offset, length,
32889d57b94SAlan Somers 		FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
32989d57b94SAlan Somers 
33089d57b94SAlan Somers 	fd = open(FULLPATH, O_RDWR);
33189d57b94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
33289d57b94SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
33389d57b94SAlan Somers 	rqsr.r_offset = offset;
33489d57b94SAlan Somers 	rqsr.r_len = length;
33589d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
33689d57b94SAlan Somers 	EXPECT_EQ(0, rmsr.r_len);
33789d57b94SAlan Somers 	EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
33889d57b94SAlan Somers 
33989d57b94SAlan Somers 	/*
34089d57b94SAlan Somers 	 * The file's attributes should not have been invalidated, so this fstat
34189d57b94SAlan Somers 	 * will not requery the daemon.
34289d57b94SAlan Somers 	 */
34389d57b94SAlan Somers 	EXPECT_EQ(0, fstat(fd, &sb1));
34489d57b94SAlan Somers 	EXPECT_EQ(fsize, (uint64_t)sb1.st_size);
34589d57b94SAlan Somers 
34689d57b94SAlan Somers 	/* mtime and ctime should be updated */
34789d57b94SAlan Somers 	EXPECT_EQ(sb0.st_atime, sb1.st_atime);
34889d57b94SAlan Somers 	EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
34989d57b94SAlan Somers 	EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
35089d57b94SAlan Somers 
35189d57b94SAlan Somers 	leak(fd);
35289d57b94SAlan Somers }
35389d57b94SAlan Somers 
35489d57b94SAlan Somers /* The returned rqsr.r_off should be clipped at EoF */
TEST_F(Fspacectl,past_eof)35589d57b94SAlan Somers TEST_F(Fspacectl, past_eof)
35689d57b94SAlan Somers {
35789d57b94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
35889d57b94SAlan Somers 	const char RELPATH[] = "some_file.txt";
35989d57b94SAlan Somers 	struct spacectl_range rqsr, rmsr;
36089d57b94SAlan Somers 	uint64_t ino = 42;
36189d57b94SAlan Somers 	uint64_t fsize = 1000;
36289d57b94SAlan Somers 	uint64_t offset = 1500;
36389d57b94SAlan Somers 	uint64_t length = 1000;
36489d57b94SAlan Somers 	int fd;
36589d57b94SAlan Somers 
36689d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
36789d57b94SAlan Somers 	expect_open(ino, 0, 1);
36889d57b94SAlan Somers 	expect_fallocate(ino, offset, length,
36989d57b94SAlan Somers 		FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
37089d57b94SAlan Somers 
37189d57b94SAlan Somers 	fd = open(FULLPATH, O_RDWR);
37289d57b94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
37389d57b94SAlan Somers 	rqsr.r_offset = offset;
37489d57b94SAlan Somers 	rqsr.r_len = length;
37589d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
37689d57b94SAlan Somers 	EXPECT_EQ(0, rmsr.r_len);
37789d57b94SAlan Somers 	EXPECT_EQ((off_t)fsize, rmsr.r_offset);
37889d57b94SAlan Somers 
37989d57b94SAlan Somers 	leak(fd);
38089d57b94SAlan Somers }
38189d57b94SAlan Somers 
38289d57b94SAlan Somers /* The returned rqsr.r_off should be clipped at EoF */
TEST_F(Fspacectl,spans_eof)38389d57b94SAlan Somers TEST_F(Fspacectl, spans_eof)
38489d57b94SAlan Somers {
38589d57b94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
38689d57b94SAlan Somers 	const char RELPATH[] = "some_file.txt";
38789d57b94SAlan Somers 	struct spacectl_range rqsr, rmsr;
38889d57b94SAlan Somers 	uint64_t ino = 42;
38989d57b94SAlan Somers 	uint64_t fsize = 1000;
39089d57b94SAlan Somers 	uint64_t offset = 500;
39189d57b94SAlan Somers 	uint64_t length = 1000;
39289d57b94SAlan Somers 	int fd;
39389d57b94SAlan Somers 
39489d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
39589d57b94SAlan Somers 	expect_open(ino, 0, 1);
39689d57b94SAlan Somers 	expect_fallocate(ino, offset, length,
39789d57b94SAlan Somers 		FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
39889d57b94SAlan Somers 
39989d57b94SAlan Somers 	fd = open(FULLPATH, O_RDWR);
40089d57b94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
40189d57b94SAlan Somers 	rqsr.r_offset = offset;
40289d57b94SAlan Somers 	rqsr.r_len = length;
40389d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
40489d57b94SAlan Somers 	EXPECT_EQ(0, rmsr.r_len);
40589d57b94SAlan Somers 	EXPECT_EQ((off_t)fsize, rmsr.r_offset);
40689d57b94SAlan Somers 
40789d57b94SAlan Somers 	leak(fd);
40889d57b94SAlan Somers }
40989d57b94SAlan Somers 
41089d57b94SAlan Somers /*
41189d57b94SAlan Somers  * With older servers, no FUSE_FALLOCATE should be attempted.  The kernel
41289d57b94SAlan Somers  * should fall back to vop_stddeallocate.
41389d57b94SAlan Somers  */
TEST_F(Fspacectl_7_18,ok)41489d57b94SAlan Somers TEST_F(Fspacectl_7_18, ok)
41589d57b94SAlan Somers {
41689d57b94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
41789d57b94SAlan Somers 	const char RELPATH[] = "some_file.txt";
41889d57b94SAlan Somers 	struct spacectl_range rqsr, rmsr;
4198bae22bbSAlan Somers 	char *buf;
42089d57b94SAlan Somers 	uint64_t ino = 42;
42189d57b94SAlan Somers 	uint64_t fsize = 2000;
42289d57b94SAlan Somers 	uint64_t offset = 500;
42389d57b94SAlan Somers 	uint64_t length = 1000;
42489d57b94SAlan Somers 	int fd;
42589d57b94SAlan Somers 
4268bae22bbSAlan Somers 	buf = new char[length];
42789d57b94SAlan Somers 
42889d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
42989d57b94SAlan Somers 	expect_open(ino, 0, 1);
43089d57b94SAlan Somers 	expect_vop_stddeallocate(ino, offset, length);
43189d57b94SAlan Somers 
43289d57b94SAlan Somers 	fd = open(FULLPATH, O_RDWR);
43389d57b94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
43489d57b94SAlan Somers 	rqsr.r_offset = offset;
43589d57b94SAlan Somers 	rqsr.r_len = length;
43689d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
43789d57b94SAlan Somers 	EXPECT_EQ(0, rmsr.r_len);
43889d57b94SAlan Somers 	EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
43989d57b94SAlan Somers 
44089d57b94SAlan Somers 	leak(fd);
4418bae22bbSAlan Somers 	delete[] buf;
44289d57b94SAlan Somers }
44389d57b94SAlan Somers 
44489d57b94SAlan Somers /*
44589d57b94SAlan Somers  * A successful fspacectl should clear the zeroed data from the kernel cache.
44689d57b94SAlan Somers  */
TEST_P(FspacectlCache,clears_cache)44789d57b94SAlan Somers TEST_P(FspacectlCache, clears_cache)
44889d57b94SAlan Somers {
44989d57b94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
45089d57b94SAlan Somers 	const char RELPATH[] = "some_file.txt";
45189d57b94SAlan Somers 	const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
45289d57b94SAlan Somers 	struct spacectl_range rqsr, rmsr;
45389d57b94SAlan Somers 	uint64_t ino = 42;
45489d57b94SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
45589d57b94SAlan Somers 	uint64_t fsize = bufsize;
45689d57b94SAlan Somers 	uint8_t buf[bufsize];
45789d57b94SAlan Somers 	char zbuf[bufsize];
45889d57b94SAlan Somers 	uint64_t offset = 0;
45989d57b94SAlan Somers 	uint64_t length = bufsize;
46089d57b94SAlan Somers 	int fd;
46189d57b94SAlan Somers 
46289d57b94SAlan Somers 	bzero(zbuf, bufsize);
46389d57b94SAlan Somers 
46489d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
46589d57b94SAlan Somers 	expect_open(ino, 0, 1);
46689d57b94SAlan Somers 	/* NB: expectations are applied in LIFO order */
46789d57b94SAlan Somers 	expect_read(ino, 0, fsize, fsize, zbuf);
46889d57b94SAlan Somers 	expect_read(ino, 0, fsize, fsize, CONTENTS);
46989d57b94SAlan Somers 	expect_fallocate(ino, offset, length,
47089d57b94SAlan Somers 		FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
47189d57b94SAlan Somers 
47289d57b94SAlan Somers 	fd = open(FULLPATH, O_RDWR);
47389d57b94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
47489d57b94SAlan Somers 
47589d57b94SAlan Somers 	/* Populate the cache */
47689d57b94SAlan Somers 	ASSERT_EQ(fsize, (uint64_t)pread(fd, buf, bufsize, 0))
47789d57b94SAlan Somers 		<< strerror(errno);
47889d57b94SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, fsize));
47989d57b94SAlan Somers 
48089d57b94SAlan Somers 	/* Zero the file */
48189d57b94SAlan Somers 	rqsr.r_offset = offset;
48289d57b94SAlan Somers 	rqsr.r_len = length;
48389d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
48489d57b94SAlan Somers 	EXPECT_EQ(0, rmsr.r_len);
48589d57b94SAlan Somers 	EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
48689d57b94SAlan Somers 
48789d57b94SAlan Somers 	/* Read again.  This should query the daemon */
48889d57b94SAlan Somers 	ASSERT_EQ(fsize, (uint64_t)pread(fd, buf, bufsize, 0))
48989d57b94SAlan Somers 		<< strerror(errno);
49089d57b94SAlan Somers 	ASSERT_EQ(0, memcmp(buf, zbuf, fsize));
49189d57b94SAlan Somers 
49289d57b94SAlan Somers 	leak(fd);
49389d57b94SAlan Somers }
49489d57b94SAlan Somers 
495811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(FspacectlCache, FspacectlCache,
496811e0a31SEnji Cooper 	Values(Uncached, Writethrough, Writeback)
49789d57b94SAlan Somers );
49889d57b94SAlan Somers 
49989d57b94SAlan Somers /*
50089d57b94SAlan Somers  * If the server returns ENOSYS, it indicates that the server does not support
501398c88c7SAlan Somers  * FUSE_FALLOCATE.  This and future calls should return EINVAL.
502398c88c7SAlan Somers  */
TEST_F(PosixFallocate,enosys)503398c88c7SAlan Somers TEST_F(PosixFallocate, enosys)
504398c88c7SAlan Somers {
505398c88c7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
506398c88c7SAlan Somers 	const char RELPATH[] = "some_file.txt";
507398c88c7SAlan Somers 	uint64_t ino = 42;
50889d57b94SAlan Somers 	uint64_t off0 = 0;
50989d57b94SAlan Somers 	uint64_t len0 = 1000;
51089d57b94SAlan Somers 	off_t off1 = 100;
51189d57b94SAlan Somers 	off_t len1 = 200;
51289d57b94SAlan Somers 	uint64_t fsize = 500;
51389d57b94SAlan Somers 	struct spacectl_range rqsr = { .r_offset = off1, .r_len = len1 };
514398c88c7SAlan Somers 	int fd;
515398c88c7SAlan Somers 
51689d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
517398c88c7SAlan Somers 	expect_open(ino, 0, 1);
51889d57b94SAlan Somers 	expect_fallocate(ino, off0, len0, 0, ENOSYS);
51989d57b94SAlan Somers 	expect_vop_stddeallocate(ino, off1, len1);
520398c88c7SAlan Somers 
521398c88c7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
522398c88c7SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
52389d57b94SAlan Somers 	EXPECT_EQ(EINVAL, posix_fallocate(fd, off0, len0));
524398c88c7SAlan Somers 
525398c88c7SAlan Somers 	/* Subsequent calls shouldn't query the daemon*/
52689d57b94SAlan Somers 	EXPECT_EQ(EINVAL, posix_fallocate(fd, off0, len0));
52789d57b94SAlan Somers 
52889d57b94SAlan Somers 	/* Neither should VOP_DEALLOCATE query the daemon */
52989d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
530398c88c7SAlan Somers 
531398c88c7SAlan Somers 	leak(fd);
532398c88c7SAlan Somers }
533398c88c7SAlan Somers 
534398c88c7SAlan Somers /*
53589d57b94SAlan Somers  * EOPNOTSUPP means "the file system does not support fallocate with the
53689d57b94SAlan Somers  * supplied mode on this particular file".  So we should fallback, but not
53789d57b94SAlan Somers  * assume anything about whether the operation will fail on a different file or
53889d57b94SAlan Somers  * with a different mode.
539398c88c7SAlan Somers  */
TEST_F(PosixFallocate,eopnotsupp)540398c88c7SAlan Somers TEST_F(PosixFallocate, eopnotsupp)
541398c88c7SAlan Somers {
542398c88c7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
543398c88c7SAlan Somers 	const char RELPATH[] = "some_file.txt";
54489d57b94SAlan Somers 	struct spacectl_range rqsr;
545398c88c7SAlan Somers 	uint64_t ino = 42;
54689d57b94SAlan Somers 	uint64_t fsize = 2000;
547398c88c7SAlan Somers 	uint64_t offset = 0;
548398c88c7SAlan Somers 	uint64_t length = 1000;
549398c88c7SAlan Somers 	int fd;
550398c88c7SAlan Somers 
55189d57b94SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
552398c88c7SAlan Somers 	expect_open(ino, 0, 1);
55389d57b94SAlan Somers 	expect_fallocate(ino, fsize, length, 0, EOPNOTSUPP);
554398c88c7SAlan Somers 	expect_fallocate(ino, offset, length, 0, EOPNOTSUPP);
55589d57b94SAlan Somers 	expect_fallocate(ino, offset, length,
55689d57b94SAlan Somers 		FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
557398c88c7SAlan Somers 
558398c88c7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
559398c88c7SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
56089d57b94SAlan Somers 	EXPECT_EQ(EINVAL, posix_fallocate(fd, fsize, length));
56189d57b94SAlan Somers 
56289d57b94SAlan Somers 	/* Subsequent calls should still query the daemon*/
563398c88c7SAlan Somers 	EXPECT_EQ(EINVAL, posix_fallocate(fd, offset, length));
564398c88c7SAlan Somers 
56589d57b94SAlan Somers 	/* And subsequent VOP_DEALLOCATE calls should also query the daemon */
56689d57b94SAlan Somers 	rqsr.r_len = length;
56789d57b94SAlan Somers 	rqsr.r_offset = offset;
56889d57b94SAlan Somers 	EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
569398c88c7SAlan Somers 
570398c88c7SAlan Somers 	leak(fd);
571398c88c7SAlan Somers }
572398c88c7SAlan Somers 
573398c88c7SAlan Somers /* EIO is not a permanent error, and may be retried */
TEST_F(PosixFallocate,eio)574398c88c7SAlan Somers TEST_F(PosixFallocate, eio)
575398c88c7SAlan Somers {
576398c88c7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
577398c88c7SAlan Somers 	const char RELPATH[] = "some_file.txt";
578398c88c7SAlan Somers 	uint64_t ino = 42;
579398c88c7SAlan Somers 	uint64_t offset = 0;
580398c88c7SAlan Somers 	uint64_t length = 1000;
581398c88c7SAlan Somers 	int fd;
582398c88c7SAlan Somers 
583398c88c7SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
584398c88c7SAlan Somers 	expect_open(ino, 0, 1);
585398c88c7SAlan Somers 	expect_fallocate(ino, offset, length, 0, EIO);
586398c88c7SAlan Somers 
587398c88c7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
588398c88c7SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
589398c88c7SAlan Somers 	EXPECT_EQ(EIO, posix_fallocate(fd, offset, length));
590398c88c7SAlan Somers 
591398c88c7SAlan Somers 	expect_fallocate(ino, offset, length, 0, 0);
592398c88c7SAlan Somers 
593398c88c7SAlan Somers 	EXPECT_EQ(0, posix_fallocate(fd, offset, length));
594398c88c7SAlan Somers 
595398c88c7SAlan Somers 	leak(fd);
596398c88c7SAlan Somers }
597398c88c7SAlan Somers 
TEST_F(PosixFallocate,erofs)598398c88c7SAlan Somers TEST_F(PosixFallocate, erofs)
599398c88c7SAlan Somers {
600398c88c7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
601398c88c7SAlan Somers 	const char RELPATH[] = "some_file.txt";
602398c88c7SAlan Somers 	struct statfs statbuf;
603398c88c7SAlan Somers 	struct iovec *iov = NULL;
604398c88c7SAlan Somers 	int iovlen = 0;
605398c88c7SAlan Somers 	uint64_t ino = 42;
606398c88c7SAlan Somers 	uint64_t offset = 0;
607398c88c7SAlan Somers 	uint64_t length = 1000;
608398c88c7SAlan Somers 	int fd;
609398c88c7SAlan Somers 	int newflags;
610398c88c7SAlan Somers 
611398c88c7SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
612398c88c7SAlan Somers 	expect_open(ino, 0, 1);
613398c88c7SAlan Somers 	EXPECT_CALL(*m_mock, process(
614398c88c7SAlan Somers 		ResultOf([](auto in) {
615398c88c7SAlan Somers 			return (in.header.opcode == FUSE_STATFS);
616398c88c7SAlan Somers 		}, Eq(true)),
617398c88c7SAlan Somers 		_)
618398c88c7SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out)
619398c88c7SAlan Somers 	{
620398c88c7SAlan Somers 		/*
621398c88c7SAlan Somers 		 * All of the fields except f_flags are don't care, and f_flags
622398c88c7SAlan Somers 		 * is set by the VFS
623398c88c7SAlan Somers 		 */
624398c88c7SAlan Somers 		SET_OUT_HEADER_LEN(out, statfs);
625398c88c7SAlan Somers 	})));
626398c88c7SAlan Somers 
627398c88c7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
628398c88c7SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
629398c88c7SAlan Somers 
630398c88c7SAlan Somers 	/* Remount read-only */
631398c88c7SAlan Somers 	ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
632398c88c7SAlan Somers 	newflags = statbuf.f_flags | MNT_UPDATE | MNT_RDONLY;
633398c88c7SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", (void*)statbuf.f_fstypename, -1);
634398c88c7SAlan Somers 	build_iovec(&iov, &iovlen, "fspath", (void*)statbuf.f_mntonname, -1);
635398c88c7SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
636398c88c7SAlan Somers 	ASSERT_EQ(0, nmount(iov, iovlen, newflags)) << strerror(errno);
63739f5d8ddSAlan Somers 	free_iovec(&iov, &iovlen);
638398c88c7SAlan Somers 
639398c88c7SAlan Somers 	EXPECT_EQ(EROFS, posix_fallocate(fd, offset, length));
640398c88c7SAlan Somers 
641398c88c7SAlan Somers 	leak(fd);
642398c88c7SAlan Somers }
643398c88c7SAlan Somers 
TEST_F(PosixFallocate,ok)644398c88c7SAlan Somers TEST_F(PosixFallocate, ok)
645398c88c7SAlan Somers {
646398c88c7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
647398c88c7SAlan Somers 	const char RELPATH[] = "some_file.txt";
648398c88c7SAlan Somers 	struct stat sb0, sb1;
649398c88c7SAlan Somers 	uint64_t ino = 42;
650398c88c7SAlan Somers 	uint64_t offset = 0;
651398c88c7SAlan Somers 	uint64_t length = 1000;
652398c88c7SAlan Somers 	int fd;
653398c88c7SAlan Somers 
654398c88c7SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
655398c88c7SAlan Somers 	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
656398c88c7SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
657398c88c7SAlan Somers 		out.body.entry.attr.mode = S_IFREG | 0644;
658398c88c7SAlan Somers 		out.body.entry.nodeid = ino;
659398c88c7SAlan Somers 		out.body.entry.entry_valid = UINT64_MAX;
660398c88c7SAlan Somers 		out.body.entry.attr_valid = UINT64_MAX;
661398c88c7SAlan Somers 	})));
662398c88c7SAlan Somers 	expect_open(ino, 0, 1);
663398c88c7SAlan Somers 	expect_fallocate(ino, offset, length, 0, 0);
664398c88c7SAlan Somers 
665398c88c7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
666398c88c7SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
667398c88c7SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
668398c88c7SAlan Somers 	EXPECT_EQ(0, posix_fallocate(fd, offset, length));
669398c88c7SAlan Somers 	/*
670398c88c7SAlan Somers 	 * Despite the originally cached file size of zero, stat should now
671398c88c7SAlan Somers 	 * return either the new size or requery the daemon.
672398c88c7SAlan Somers 	 */
673398c88c7SAlan Somers 	EXPECT_EQ(0, stat(FULLPATH, &sb1));
674398c88c7SAlan Somers 	EXPECT_EQ(length, (uint64_t)sb1.st_size);
675398c88c7SAlan Somers 
676398c88c7SAlan Somers 	/* mtime and ctime should be updated */
677398c88c7SAlan Somers 	EXPECT_EQ(sb0.st_atime, sb1.st_atime);
678398c88c7SAlan Somers 	EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
679398c88c7SAlan Somers 	EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
680398c88c7SAlan Somers 
681398c88c7SAlan Somers 	leak(fd);
682398c88c7SAlan Somers }
683398c88c7SAlan Somers 
684398c88c7SAlan Somers /* fusefs should respect RLIMIT_FSIZE */
TEST_F(PosixFallocate,rlimit_fsize)685398c88c7SAlan Somers TEST_F(PosixFallocate, rlimit_fsize)
686398c88c7SAlan Somers {
687398c88c7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
688398c88c7SAlan Somers 	const char RELPATH[] = "some_file.txt";
689398c88c7SAlan Somers 	struct rlimit rl;
690398c88c7SAlan Somers 	uint64_t ino = 42;
691398c88c7SAlan Somers 	uint64_t offset = 0;
692398c88c7SAlan Somers 	uint64_t length = 1'000'000;
693398c88c7SAlan Somers 	int fd;
694398c88c7SAlan Somers 
695398c88c7SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
696398c88c7SAlan Somers 	expect_open(ino, 0, 1);
697398c88c7SAlan Somers 
698398c88c7SAlan Somers 	rl.rlim_cur = length / 2;
699398c88c7SAlan Somers 	rl.rlim_max = 10 * length;
700398c88c7SAlan Somers 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
701398c88c7SAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
702398c88c7SAlan Somers 
703398c88c7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
704398c88c7SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
705398c88c7SAlan Somers 	EXPECT_EQ(EFBIG, posix_fallocate(fd, offset, length));
706398c88c7SAlan Somers 	EXPECT_EQ(1, s_sigxfsz);
707398c88c7SAlan Somers 
708398c88c7SAlan Somers 	leak(fd);
709398c88c7SAlan Somers }
710398c88c7SAlan Somers 
711398c88c7SAlan Somers /* With older servers, no FUSE_FALLOCATE should be attempted */
TEST_F(PosixFallocate_7_18,einval)712398c88c7SAlan Somers TEST_F(PosixFallocate_7_18, einval)
713398c88c7SAlan Somers {
714398c88c7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
715398c88c7SAlan Somers 	const char RELPATH[] = "some_file.txt";
716398c88c7SAlan Somers 	uint64_t ino = 42;
717398c88c7SAlan Somers 	uint64_t offset = 0;
718398c88c7SAlan Somers 	uint64_t length = 1000;
719398c88c7SAlan Somers 	int fd;
720398c88c7SAlan Somers 
721398c88c7SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
722398c88c7SAlan Somers 	expect_open(ino, 0, 1);
723398c88c7SAlan Somers 
724398c88c7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
725398c88c7SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
726398c88c7SAlan Somers 	EXPECT_EQ(EINVAL, posix_fallocate(fd, offset, length));
727398c88c7SAlan Somers 
728398c88c7SAlan Somers 	leak(fd);
729398c88c7SAlan Somers }
730