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>
35*a123502eSBrooks Davis #include <mntopts.h> // for build_iovec
36398c88c7SAlan Somers #include <signal.h>
37398c88c7SAlan Somers #include <unistd.h>
38398c88c7SAlan Somers }
39398c88c7SAlan Somers
40398c88c7SAlan Somers #include "mockfs.hh"
41398c88c7SAlan Somers #include "utils.hh"
42398c88c7SAlan Somers
43398c88c7SAlan Somers using namespace testing;
44398c88c7SAlan Somers
4589d57b94SAlan Somers /* Is buf all zero? */
4689d57b94SAlan Somers static bool
is_zero(const char * buf,uint64_t size)4789d57b94SAlan Somers is_zero(const char *buf, uint64_t size)
4889d57b94SAlan Somers {
4989d57b94SAlan Somers return buf[0] == 0 && !memcmp(buf, buf + 1, size - 1);
5089d57b94SAlan Somers }
5189d57b94SAlan Somers
5289d57b94SAlan Somers class Fallocate: public FuseTest {
5389d57b94SAlan Somers public:
5489d57b94SAlan Somers /*
5589d57b94SAlan Somers * expect VOP_DEALLOCATE to be implemented by vop_stddeallocate.
5689d57b94SAlan Somers */
expect_vop_stddeallocate(uint64_t ino,uint64_t off,uint64_t length)5789d57b94SAlan Somers void expect_vop_stddeallocate(uint64_t ino, uint64_t off, uint64_t length)
5889d57b94SAlan Somers {
5989d57b94SAlan Somers /* XXX read offset and size may depend on cache mode */
6089d57b94SAlan Somers EXPECT_CALL(*m_mock, process(
6189d57b94SAlan Somers ResultOf([=](auto in) {
6289d57b94SAlan Somers return (in.header.opcode == FUSE_READ &&
6389d57b94SAlan Somers in.header.nodeid == ino &&
6489d57b94SAlan Somers in.body.read.offset <= off &&
6589d57b94SAlan Somers in.body.read.offset + in.body.read.size >=
6689d57b94SAlan Somers off + length);
6789d57b94SAlan Somers }, Eq(true)),
6889d57b94SAlan Somers _)
6989d57b94SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
700c9df4afSAlan Somers assert(in.body.read.size <= sizeof(out.body.bytes));
7189d57b94SAlan Somers out.header.len = sizeof(struct fuse_out_header) +
7289d57b94SAlan Somers in.body.read.size;
7389d57b94SAlan Somers memset(out.body.bytes, 'X', in.body.read.size);
7489d57b94SAlan Somers }))).RetiresOnSaturation();
7589d57b94SAlan Somers EXPECT_CALL(*m_mock, process(
7689d57b94SAlan Somers ResultOf([=](auto in) {
7789d57b94SAlan Somers const char *buf = (const char*)in.body.bytes +
7889d57b94SAlan Somers sizeof(struct fuse_write_in);
7989d57b94SAlan Somers
800c9df4afSAlan Somers assert(length <= sizeof(in.body.bytes) -
810c9df4afSAlan Somers sizeof(struct fuse_write_in));
8289d57b94SAlan Somers return (in.header.opcode == FUSE_WRITE &&
8389d57b94SAlan Somers in.header.nodeid == ino &&
8489d57b94SAlan Somers in.body.write.offset == off &&
8589d57b94SAlan Somers in.body.write.size == length &&
8689d57b94SAlan Somers is_zero(buf, length));
8789d57b94SAlan Somers }, Eq(true)),
8889d57b94SAlan Somers _)
8989d57b94SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
9089d57b94SAlan Somers SET_OUT_HEADER_LEN(out, write);
9189d57b94SAlan Somers out.body.write.size = length;
9289d57b94SAlan Somers })));
9389d57b94SAlan Somers }
9489d57b94SAlan Somers };
9589d57b94SAlan Somers
9689d57b94SAlan Somers class Fspacectl: public Fallocate {};
9789d57b94SAlan Somers
9889d57b94SAlan Somers class Fspacectl_7_18: public Fspacectl {
9989d57b94SAlan Somers public:
SetUp()10089d57b94SAlan Somers virtual void SetUp() {
10189d57b94SAlan Somers m_kernel_minor_version = 18;
10289d57b94SAlan Somers Fspacectl::SetUp();
10389d57b94SAlan Somers }
10489d57b94SAlan Somers };
10589d57b94SAlan Somers
10689d57b94SAlan Somers class FspacectlCache: public Fspacectl, public WithParamInterface<cache_mode> {
10789d57b94SAlan Somers public:
10889d57b94SAlan Somers bool m_direct_io;
10989d57b94SAlan Somers
FspacectlCache()11089d57b94SAlan Somers FspacectlCache(): m_direct_io(false) {};
11189d57b94SAlan Somers
SetUp()11289d57b94SAlan Somers virtual void SetUp() {
11389d57b94SAlan Somers int cache_mode = GetParam();
11489d57b94SAlan Somers switch (cache_mode) {
11589d57b94SAlan Somers case Uncached:
11689d57b94SAlan Somers m_direct_io = true;
11789d57b94SAlan Somers break;
11889d57b94SAlan Somers case WritebackAsync:
11989d57b94SAlan Somers m_async = true;
12089d57b94SAlan Somers /* FALLTHROUGH */
12189d57b94SAlan Somers case Writeback:
12289d57b94SAlan Somers m_init_flags |= FUSE_WRITEBACK_CACHE;
12389d57b94SAlan Somers /* FALLTHROUGH */
12489d57b94SAlan Somers case Writethrough:
12589d57b94SAlan Somers break;
12689d57b94SAlan Somers default:
12789d57b94SAlan Somers FAIL() << "Unknown cache mode";
12889d57b94SAlan Somers }
12989d57b94SAlan Somers
13089d57b94SAlan Somers FuseTest::SetUp();
13189d57b94SAlan Somers if (IsSkipped())
13289d57b94SAlan Somers return;
13389d57b94SAlan Somers }
13489d57b94SAlan Somers };
135398c88c7SAlan Somers
136398c88c7SAlan Somers class PosixFallocate: public Fallocate {
137398c88c7SAlan Somers public:
138398c88c7SAlan Somers static sig_atomic_t s_sigxfsz;
139398c88c7SAlan Somers
SetUp()140398c88c7SAlan Somers void SetUp() {
141398c88c7SAlan Somers s_sigxfsz = 0;
142398c88c7SAlan Somers FuseTest::SetUp();
143398c88c7SAlan Somers }
144398c88c7SAlan Somers
TearDown()145398c88c7SAlan Somers void TearDown() {
146398c88c7SAlan Somers struct sigaction sa;
147398c88c7SAlan Somers
148398c88c7SAlan Somers bzero(&sa, sizeof(sa));
149398c88c7SAlan Somers sa.sa_handler = SIG_DFL;
150398c88c7SAlan Somers sigaction(SIGXFSZ, &sa, NULL);
151398c88c7SAlan Somers
152398c88c7SAlan Somers Fallocate::TearDown();
153398c88c7SAlan Somers }
154398c88c7SAlan Somers
155398c88c7SAlan Somers };
156398c88c7SAlan Somers
157398c88c7SAlan Somers sig_atomic_t PosixFallocate::s_sigxfsz = 0;
158398c88c7SAlan Somers
sigxfsz_handler(int __unused sig)159398c88c7SAlan Somers void sigxfsz_handler(int __unused sig) {
160398c88c7SAlan Somers PosixFallocate::s_sigxfsz = 1;
161398c88c7SAlan Somers }
162398c88c7SAlan Somers
163398c88c7SAlan Somers class PosixFallocate_7_18: public PosixFallocate {
164398c88c7SAlan Somers public:
SetUp()165398c88c7SAlan Somers virtual void SetUp() {
166398c88c7SAlan Somers m_kernel_minor_version = 18;
167398c88c7SAlan Somers PosixFallocate::SetUp();
168398c88c7SAlan Somers }
169398c88c7SAlan Somers };
170398c88c7SAlan Somers
171398c88c7SAlan Somers
172398c88c7SAlan Somers /*
173398c88c7SAlan Somers * If the server returns ENOSYS, it indicates that the server does not support
17489d57b94SAlan Somers * FUSE_FALLOCATE. This and future calls should fall back to vop_stddeallocate.
17589d57b94SAlan Somers */
TEST_F(Fspacectl,enosys)17689d57b94SAlan Somers TEST_F(Fspacectl, enosys)
17789d57b94SAlan Somers {
17889d57b94SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
17989d57b94SAlan Somers const char RELPATH[] = "some_file.txt";
18089d57b94SAlan Somers off_t fsize = 1 << 20;
18189d57b94SAlan Somers off_t off0 = 100;
18289d57b94SAlan Somers off_t len0 = 500;
18389d57b94SAlan Somers struct spacectl_range rqsr = { .r_offset = off0, .r_len = len0 };
18489d57b94SAlan Somers uint64_t ino = 42;
18589d57b94SAlan Somers uint64_t off1 = fsize;
18689d57b94SAlan Somers uint64_t len1 = 1000;
18789d57b94SAlan Somers off_t off2 = fsize / 2;
18889d57b94SAlan Somers off_t len2 = 500;
18989d57b94SAlan Somers int fd;
19089d57b94SAlan Somers
19189d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
19289d57b94SAlan Somers expect_open(ino, 0, 1);
19389d57b94SAlan Somers expect_fallocate(ino, off0, len0,
19489d57b94SAlan Somers FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, ENOSYS);
19589d57b94SAlan Somers expect_vop_stddeallocate(ino, off0, len0);
19689d57b94SAlan Somers expect_vop_stddeallocate(ino, off2, len2);
19789d57b94SAlan Somers
19889d57b94SAlan Somers fd = open(FULLPATH, O_RDWR);
19989d57b94SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
20089d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
20189d57b94SAlan Somers
20289d57b94SAlan Somers /* Subsequent calls shouldn't query the daemon either */
20389d57b94SAlan Somers rqsr.r_offset = off2;
20489d57b94SAlan Somers rqsr.r_len = len2;
20589d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
20689d57b94SAlan Somers
20789d57b94SAlan Somers /* Neither should posix_fallocate query the daemon */
20889d57b94SAlan Somers EXPECT_EQ(EINVAL, posix_fallocate(fd, off1, len1));
20989d57b94SAlan Somers
21089d57b94SAlan Somers leak(fd);
21189d57b94SAlan Somers }
21289d57b94SAlan Somers
21389d57b94SAlan Somers /*
21489d57b94SAlan Somers * EOPNOTSUPP means "the file system does not support fallocate with the
21589d57b94SAlan Somers * supplied mode on this particular file". So we should fallback, but not
21689d57b94SAlan Somers * assume anything about whether the operation will fail on a different file or
21789d57b94SAlan Somers * with a different mode.
21889d57b94SAlan Somers */
TEST_F(Fspacectl,eopnotsupp)21989d57b94SAlan Somers TEST_F(Fspacectl, eopnotsupp)
22089d57b94SAlan Somers {
22189d57b94SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
22289d57b94SAlan Somers const char RELPATH[] = "some_file.txt";
22389d57b94SAlan Somers struct spacectl_range rqsr;
22489d57b94SAlan Somers uint64_t ino = 42;
22589d57b94SAlan Somers uint64_t fsize = 1 << 20;
22689d57b94SAlan Somers uint64_t off0 = 500;
22789d57b94SAlan Somers uint64_t len = 1000;
22889d57b94SAlan Somers uint64_t off1 = fsize / 2;
22989d57b94SAlan Somers int fd;
23089d57b94SAlan Somers
23189d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
23289d57b94SAlan Somers expect_open(ino, 0, 1);
23389d57b94SAlan Somers expect_fallocate(ino, off0, len,
23489d57b94SAlan Somers FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE,
23589d57b94SAlan Somers EOPNOTSUPP);
23689d57b94SAlan Somers expect_vop_stddeallocate(ino, off0, len);
23789d57b94SAlan Somers expect_fallocate(ino, off1, len,
23889d57b94SAlan Somers FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE,
23989d57b94SAlan Somers EOPNOTSUPP);
24089d57b94SAlan Somers expect_vop_stddeallocate(ino, off1, len);
24189d57b94SAlan Somers expect_fallocate(ino, fsize, len, 0, 0);
24289d57b94SAlan Somers
24389d57b94SAlan Somers fd = open(FULLPATH, O_RDWR);
24489d57b94SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
24589d57b94SAlan Somers
24689d57b94SAlan Somers /*
24789d57b94SAlan Somers * Though the FUSE daemon will reject the call, the kernel should fall
24889d57b94SAlan Somers * back to a read-modify-write approach.
24989d57b94SAlan Somers */
25089d57b94SAlan Somers rqsr.r_offset = off0;
25189d57b94SAlan Somers rqsr.r_len = len;
25289d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
25389d57b94SAlan Somers
25489d57b94SAlan Somers /* Subsequent calls should still query the daemon */
25589d57b94SAlan Somers rqsr.r_offset = off1;
25689d57b94SAlan Somers rqsr.r_len = len;
25789d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
25889d57b94SAlan Somers
25989d57b94SAlan Somers /* But subsequent posix_fallocate calls _should_ query the daemon */
26089d57b94SAlan Somers EXPECT_EQ(0, posix_fallocate(fd, fsize, len));
26189d57b94SAlan Somers
26289d57b94SAlan Somers leak(fd);
26389d57b94SAlan Somers }
26489d57b94SAlan Somers
TEST_F(Fspacectl,erofs)26589d57b94SAlan Somers TEST_F(Fspacectl, erofs)
26689d57b94SAlan Somers {
26789d57b94SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
26889d57b94SAlan Somers const char RELPATH[] = "some_file.txt";
26989d57b94SAlan Somers struct statfs statbuf;
27089d57b94SAlan Somers uint64_t fsize = 2000;
27189d57b94SAlan Somers struct spacectl_range rqsr = { .r_offset = 0, .r_len = 1 };
27289d57b94SAlan Somers struct iovec *iov = NULL;
27389d57b94SAlan Somers int iovlen = 0;
27489d57b94SAlan Somers uint64_t ino = 42;
27589d57b94SAlan Somers int fd;
27689d57b94SAlan Somers int newflags;
27789d57b94SAlan Somers
27889d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
27989d57b94SAlan Somers expect_open(ino, 0, 1);
28089d57b94SAlan Somers EXPECT_CALL(*m_mock, process(
28189d57b94SAlan Somers ResultOf([](auto in) {
28289d57b94SAlan Somers return (in.header.opcode == FUSE_STATFS);
28389d57b94SAlan Somers }, Eq(true)),
28489d57b94SAlan Somers _)
28589d57b94SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out)
28689d57b94SAlan Somers {
28789d57b94SAlan Somers /*
28889d57b94SAlan Somers * All of the fields except f_flags are don't care, and f_flags
28989d57b94SAlan Somers * is set by the VFS
29089d57b94SAlan Somers */
29189d57b94SAlan Somers SET_OUT_HEADER_LEN(out, statfs);
29289d57b94SAlan Somers })));
29389d57b94SAlan Somers
29489d57b94SAlan Somers fd = open(FULLPATH, O_RDWR);
29589d57b94SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
29689d57b94SAlan Somers
29789d57b94SAlan Somers /* Remount read-only */
29889d57b94SAlan Somers ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
29989d57b94SAlan Somers newflags = statbuf.f_flags | MNT_UPDATE | MNT_RDONLY;
30089d57b94SAlan Somers build_iovec(&iov, &iovlen, "fstype", (void*)statbuf.f_fstypename, -1);
30189d57b94SAlan Somers build_iovec(&iov, &iovlen, "fspath", (void*)statbuf.f_mntonname, -1);
30289d57b94SAlan Somers build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
30389d57b94SAlan Somers ASSERT_EQ(0, nmount(iov, iovlen, newflags)) << strerror(errno);
30439f5d8ddSAlan Somers free_iovec(&iov, &iovlen);
30589d57b94SAlan Somers
30689d57b94SAlan Somers EXPECT_EQ(-1, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
30789d57b94SAlan Somers EXPECT_EQ(EROFS, errno);
30889d57b94SAlan Somers
30989d57b94SAlan Somers leak(fd);
31089d57b94SAlan Somers }
31189d57b94SAlan Somers
312f93a50d6SAlan Somers /*
313f93a50d6SAlan Somers * If FUSE_GETATTR fails when determining the size of the file, fspacectl
314f93a50d6SAlan Somers * should fail gracefully. This failure mode is easiest to trigger when
315f93a50d6SAlan Somers * attribute caching is disabled.
316f93a50d6SAlan Somers */
TEST_F(Fspacectl,getattr_fails)317f93a50d6SAlan Somers TEST_F(Fspacectl, getattr_fails)
318f93a50d6SAlan Somers {
319f93a50d6SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
320f93a50d6SAlan Somers const char RELPATH[] = "some_file.txt";
321f93a50d6SAlan Somers Sequence seq;
322f93a50d6SAlan Somers struct spacectl_range rqsr;
323f93a50d6SAlan Somers const uint64_t ino = 42;
324f93a50d6SAlan Somers const uint64_t fsize = 2000;
325f93a50d6SAlan Somers int fd;
326f93a50d6SAlan Somers
327f93a50d6SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1, 0);
328f93a50d6SAlan Somers expect_open(ino, 0, 1);
329f93a50d6SAlan Somers EXPECT_CALL(*m_mock, process(
330f93a50d6SAlan Somers ResultOf([](auto in) {
331f93a50d6SAlan Somers return (in.header.opcode == FUSE_GETATTR &&
332f93a50d6SAlan Somers in.header.nodeid == ino);
333f93a50d6SAlan Somers }, Eq(true)),
334f93a50d6SAlan Somers _)
335f93a50d6SAlan Somers ).Times(1)
336f93a50d6SAlan Somers .InSequence(seq)
337f93a50d6SAlan Somers .WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
338f93a50d6SAlan Somers SET_OUT_HEADER_LEN(out, attr);
339f93a50d6SAlan Somers out.body.attr.attr.ino = ino;
340f93a50d6SAlan Somers out.body.attr.attr.mode = S_IFREG | 0644;
341f93a50d6SAlan Somers out.body.attr.attr.size = fsize;
342f93a50d6SAlan Somers out.body.attr.attr_valid = 0;
343f93a50d6SAlan Somers })));
344f93a50d6SAlan Somers EXPECT_CALL(*m_mock, process(
345f93a50d6SAlan Somers ResultOf([](auto in) {
346f93a50d6SAlan Somers return (in.header.opcode == FUSE_GETATTR &&
347f93a50d6SAlan Somers in.header.nodeid == ino);
348f93a50d6SAlan Somers }, Eq(true)),
349f93a50d6SAlan Somers _)
350f93a50d6SAlan Somers ).InSequence(seq)
351f93a50d6SAlan Somers .WillOnce(ReturnErrno(EIO));
352f93a50d6SAlan Somers
353f93a50d6SAlan Somers fd = open(FULLPATH, O_RDWR);
354f93a50d6SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
355f93a50d6SAlan Somers rqsr.r_offset = 500;
356f93a50d6SAlan Somers rqsr.r_len = 1000;
357f93a50d6SAlan Somers EXPECT_EQ(-1, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
358f93a50d6SAlan Somers EXPECT_EQ(EIO, errno);
359f93a50d6SAlan Somers
360f93a50d6SAlan Somers leak(fd);
361f93a50d6SAlan Somers }
362f93a50d6SAlan Somers
TEST_F(Fspacectl,ok)36389d57b94SAlan Somers TEST_F(Fspacectl, ok)
36489d57b94SAlan Somers {
36589d57b94SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
36689d57b94SAlan Somers const char RELPATH[] = "some_file.txt";
36789d57b94SAlan Somers struct spacectl_range rqsr, rmsr;
36889d57b94SAlan Somers struct stat sb0, sb1;
36989d57b94SAlan Somers uint64_t ino = 42;
37089d57b94SAlan Somers uint64_t fsize = 2000;
37189d57b94SAlan Somers uint64_t offset = 500;
37289d57b94SAlan Somers uint64_t length = 1000;
37389d57b94SAlan Somers int fd;
37489d57b94SAlan Somers
37589d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
37689d57b94SAlan Somers expect_open(ino, 0, 1);
37789d57b94SAlan Somers expect_fallocate(ino, offset, length,
37889d57b94SAlan Somers FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
37989d57b94SAlan Somers
38089d57b94SAlan Somers fd = open(FULLPATH, O_RDWR);
38189d57b94SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
38289d57b94SAlan Somers ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
38389d57b94SAlan Somers rqsr.r_offset = offset;
38489d57b94SAlan Somers rqsr.r_len = length;
38589d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
38689d57b94SAlan Somers EXPECT_EQ(0, rmsr.r_len);
38789d57b94SAlan Somers EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
38889d57b94SAlan Somers
38989d57b94SAlan Somers /*
39089d57b94SAlan Somers * The file's attributes should not have been invalidated, so this fstat
39189d57b94SAlan Somers * will not requery the daemon.
39289d57b94SAlan Somers */
39389d57b94SAlan Somers EXPECT_EQ(0, fstat(fd, &sb1));
39489d57b94SAlan Somers EXPECT_EQ(fsize, (uint64_t)sb1.st_size);
39589d57b94SAlan Somers
39689d57b94SAlan Somers /* mtime and ctime should be updated */
39789d57b94SAlan Somers EXPECT_EQ(sb0.st_atime, sb1.st_atime);
39889d57b94SAlan Somers EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
39989d57b94SAlan Somers EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
40089d57b94SAlan Somers
40189d57b94SAlan Somers leak(fd);
40289d57b94SAlan Somers }
40389d57b94SAlan Somers
40489d57b94SAlan Somers /* The returned rqsr.r_off should be clipped at EoF */
TEST_F(Fspacectl,past_eof)40589d57b94SAlan Somers TEST_F(Fspacectl, past_eof)
40689d57b94SAlan Somers {
40789d57b94SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
40889d57b94SAlan Somers const char RELPATH[] = "some_file.txt";
40989d57b94SAlan Somers struct spacectl_range rqsr, rmsr;
41089d57b94SAlan Somers uint64_t ino = 42;
41189d57b94SAlan Somers uint64_t fsize = 1000;
41289d57b94SAlan Somers uint64_t offset = 1500;
41389d57b94SAlan Somers uint64_t length = 1000;
41489d57b94SAlan Somers int fd;
41589d57b94SAlan Somers
41689d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
41789d57b94SAlan Somers expect_open(ino, 0, 1);
41889d57b94SAlan Somers expect_fallocate(ino, offset, length,
41989d57b94SAlan Somers FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
42089d57b94SAlan Somers
42189d57b94SAlan Somers fd = open(FULLPATH, O_RDWR);
42289d57b94SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
42389d57b94SAlan Somers rqsr.r_offset = offset;
42489d57b94SAlan Somers rqsr.r_len = length;
42589d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
42689d57b94SAlan Somers EXPECT_EQ(0, rmsr.r_len);
42789d57b94SAlan Somers EXPECT_EQ((off_t)fsize, rmsr.r_offset);
42889d57b94SAlan Somers
42989d57b94SAlan Somers leak(fd);
43089d57b94SAlan Somers }
43189d57b94SAlan Somers
43289d57b94SAlan Somers /* The returned rqsr.r_off should be clipped at EoF */
TEST_F(Fspacectl,spans_eof)43389d57b94SAlan Somers TEST_F(Fspacectl, spans_eof)
43489d57b94SAlan Somers {
43589d57b94SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
43689d57b94SAlan Somers const char RELPATH[] = "some_file.txt";
43789d57b94SAlan Somers struct spacectl_range rqsr, rmsr;
43889d57b94SAlan Somers uint64_t ino = 42;
43989d57b94SAlan Somers uint64_t fsize = 1000;
44089d57b94SAlan Somers uint64_t offset = 500;
44189d57b94SAlan Somers uint64_t length = 1000;
44289d57b94SAlan Somers int fd;
44389d57b94SAlan Somers
44489d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
44589d57b94SAlan Somers expect_open(ino, 0, 1);
44689d57b94SAlan Somers expect_fallocate(ino, offset, length,
44789d57b94SAlan Somers FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
44889d57b94SAlan Somers
44989d57b94SAlan Somers fd = open(FULLPATH, O_RDWR);
45089d57b94SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
45189d57b94SAlan Somers rqsr.r_offset = offset;
45289d57b94SAlan Somers rqsr.r_len = length;
45389d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
45489d57b94SAlan Somers EXPECT_EQ(0, rmsr.r_len);
45589d57b94SAlan Somers EXPECT_EQ((off_t)fsize, rmsr.r_offset);
45689d57b94SAlan Somers
45789d57b94SAlan Somers leak(fd);
45889d57b94SAlan Somers }
45989d57b94SAlan Somers
46089d57b94SAlan Somers /*
46189d57b94SAlan Somers * With older servers, no FUSE_FALLOCATE should be attempted. The kernel
46289d57b94SAlan Somers * should fall back to vop_stddeallocate.
46389d57b94SAlan Somers */
TEST_F(Fspacectl_7_18,ok)46489d57b94SAlan Somers TEST_F(Fspacectl_7_18, ok)
46589d57b94SAlan Somers {
46689d57b94SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
46789d57b94SAlan Somers const char RELPATH[] = "some_file.txt";
46889d57b94SAlan Somers struct spacectl_range rqsr, rmsr;
4698bae22bbSAlan Somers char *buf;
47089d57b94SAlan Somers uint64_t ino = 42;
47189d57b94SAlan Somers uint64_t fsize = 2000;
47289d57b94SAlan Somers uint64_t offset = 500;
47389d57b94SAlan Somers uint64_t length = 1000;
47489d57b94SAlan Somers int fd;
47589d57b94SAlan Somers
4768bae22bbSAlan Somers buf = new char[length];
47789d57b94SAlan Somers
47889d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
47989d57b94SAlan Somers expect_open(ino, 0, 1);
48089d57b94SAlan Somers expect_vop_stddeallocate(ino, offset, length);
48189d57b94SAlan Somers
48289d57b94SAlan Somers fd = open(FULLPATH, O_RDWR);
48389d57b94SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
48489d57b94SAlan Somers rqsr.r_offset = offset;
48589d57b94SAlan Somers rqsr.r_len = length;
48689d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
48789d57b94SAlan Somers EXPECT_EQ(0, rmsr.r_len);
48889d57b94SAlan Somers EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
48989d57b94SAlan Somers
49089d57b94SAlan Somers leak(fd);
4918bae22bbSAlan Somers delete[] buf;
49289d57b94SAlan Somers }
49389d57b94SAlan Somers
49489d57b94SAlan Somers /*
49589d57b94SAlan Somers * A successful fspacectl should clear the zeroed data from the kernel cache.
49689d57b94SAlan Somers */
TEST_P(FspacectlCache,clears_cache)49789d57b94SAlan Somers TEST_P(FspacectlCache, clears_cache)
49889d57b94SAlan Somers {
49989d57b94SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
50089d57b94SAlan Somers const char RELPATH[] = "some_file.txt";
50189d57b94SAlan Somers const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
50289d57b94SAlan Somers struct spacectl_range rqsr, rmsr;
50389d57b94SAlan Somers uint64_t ino = 42;
50489d57b94SAlan Somers ssize_t bufsize = strlen(CONTENTS);
50589d57b94SAlan Somers uint64_t fsize = bufsize;
50689d57b94SAlan Somers uint8_t buf[bufsize];
50789d57b94SAlan Somers char zbuf[bufsize];
50889d57b94SAlan Somers uint64_t offset = 0;
50989d57b94SAlan Somers uint64_t length = bufsize;
51089d57b94SAlan Somers int fd;
51189d57b94SAlan Somers
51289d57b94SAlan Somers bzero(zbuf, bufsize);
51389d57b94SAlan Somers
51489d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
51589d57b94SAlan Somers expect_open(ino, 0, 1);
51689d57b94SAlan Somers /* NB: expectations are applied in LIFO order */
51789d57b94SAlan Somers expect_read(ino, 0, fsize, fsize, zbuf);
51889d57b94SAlan Somers expect_read(ino, 0, fsize, fsize, CONTENTS);
51989d57b94SAlan Somers expect_fallocate(ino, offset, length,
52089d57b94SAlan Somers FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
52189d57b94SAlan Somers
52289d57b94SAlan Somers fd = open(FULLPATH, O_RDWR);
52389d57b94SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
52489d57b94SAlan Somers
52589d57b94SAlan Somers /* Populate the cache */
52689d57b94SAlan Somers ASSERT_EQ(fsize, (uint64_t)pread(fd, buf, bufsize, 0))
52789d57b94SAlan Somers << strerror(errno);
52889d57b94SAlan Somers ASSERT_EQ(0, memcmp(buf, CONTENTS, fsize));
52989d57b94SAlan Somers
53089d57b94SAlan Somers /* Zero the file */
53189d57b94SAlan Somers rqsr.r_offset = offset;
53289d57b94SAlan Somers rqsr.r_len = length;
53389d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
53489d57b94SAlan Somers EXPECT_EQ(0, rmsr.r_len);
53589d57b94SAlan Somers EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
53689d57b94SAlan Somers
53789d57b94SAlan Somers /* Read again. This should query the daemon */
53889d57b94SAlan Somers ASSERT_EQ(fsize, (uint64_t)pread(fd, buf, bufsize, 0))
53989d57b94SAlan Somers << strerror(errno);
54089d57b94SAlan Somers ASSERT_EQ(0, memcmp(buf, zbuf, fsize));
54189d57b94SAlan Somers
54289d57b94SAlan Somers leak(fd);
54389d57b94SAlan Somers }
54489d57b94SAlan Somers
545811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(FspacectlCache, FspacectlCache,
546811e0a31SEnji Cooper Values(Uncached, Writethrough, Writeback)
54789d57b94SAlan Somers );
54889d57b94SAlan Somers
54989d57b94SAlan Somers /*
55089d57b94SAlan Somers * If the server returns ENOSYS, it indicates that the server does not support
551398c88c7SAlan Somers * FUSE_FALLOCATE. This and future calls should return EINVAL.
552398c88c7SAlan Somers */
TEST_F(PosixFallocate,enosys)553398c88c7SAlan Somers TEST_F(PosixFallocate, enosys)
554398c88c7SAlan Somers {
555398c88c7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
556398c88c7SAlan Somers const char RELPATH[] = "some_file.txt";
557398c88c7SAlan Somers uint64_t ino = 42;
55889d57b94SAlan Somers uint64_t off0 = 0;
55989d57b94SAlan Somers uint64_t len0 = 1000;
56089d57b94SAlan Somers off_t off1 = 100;
56189d57b94SAlan Somers off_t len1 = 200;
56289d57b94SAlan Somers uint64_t fsize = 500;
56389d57b94SAlan Somers struct spacectl_range rqsr = { .r_offset = off1, .r_len = len1 };
564398c88c7SAlan Somers int fd;
565398c88c7SAlan Somers
56689d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
567398c88c7SAlan Somers expect_open(ino, 0, 1);
56889d57b94SAlan Somers expect_fallocate(ino, off0, len0, 0, ENOSYS);
56989d57b94SAlan Somers expect_vop_stddeallocate(ino, off1, len1);
570398c88c7SAlan Somers
571398c88c7SAlan Somers fd = open(FULLPATH, O_RDWR);
572398c88c7SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
57389d57b94SAlan Somers EXPECT_EQ(EINVAL, posix_fallocate(fd, off0, len0));
574398c88c7SAlan Somers
575398c88c7SAlan Somers /* Subsequent calls shouldn't query the daemon*/
57689d57b94SAlan Somers EXPECT_EQ(EINVAL, posix_fallocate(fd, off0, len0));
57789d57b94SAlan Somers
57889d57b94SAlan Somers /* Neither should VOP_DEALLOCATE query the daemon */
57989d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
580398c88c7SAlan Somers
581398c88c7SAlan Somers leak(fd);
582398c88c7SAlan Somers }
583398c88c7SAlan Somers
584398c88c7SAlan Somers /*
58589d57b94SAlan Somers * EOPNOTSUPP means "the file system does not support fallocate with the
58689d57b94SAlan Somers * supplied mode on this particular file". So we should fallback, but not
58789d57b94SAlan Somers * assume anything about whether the operation will fail on a different file or
58889d57b94SAlan Somers * with a different mode.
589398c88c7SAlan Somers */
TEST_F(PosixFallocate,eopnotsupp)590398c88c7SAlan Somers TEST_F(PosixFallocate, eopnotsupp)
591398c88c7SAlan Somers {
592398c88c7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
593398c88c7SAlan Somers const char RELPATH[] = "some_file.txt";
59489d57b94SAlan Somers struct spacectl_range rqsr;
595398c88c7SAlan Somers uint64_t ino = 42;
59689d57b94SAlan Somers uint64_t fsize = 2000;
597398c88c7SAlan Somers uint64_t offset = 0;
598398c88c7SAlan Somers uint64_t length = 1000;
599398c88c7SAlan Somers int fd;
600398c88c7SAlan Somers
60189d57b94SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
602398c88c7SAlan Somers expect_open(ino, 0, 1);
60389d57b94SAlan Somers expect_fallocate(ino, fsize, length, 0, EOPNOTSUPP);
604398c88c7SAlan Somers expect_fallocate(ino, offset, length, 0, EOPNOTSUPP);
60589d57b94SAlan Somers expect_fallocate(ino, offset, length,
60689d57b94SAlan Somers FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
607398c88c7SAlan Somers
608398c88c7SAlan Somers fd = open(FULLPATH, O_RDWR);
609398c88c7SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
61089d57b94SAlan Somers EXPECT_EQ(EINVAL, posix_fallocate(fd, fsize, length));
61189d57b94SAlan Somers
61289d57b94SAlan Somers /* Subsequent calls should still query the daemon*/
613398c88c7SAlan Somers EXPECT_EQ(EINVAL, posix_fallocate(fd, offset, length));
614398c88c7SAlan Somers
61589d57b94SAlan Somers /* And subsequent VOP_DEALLOCATE calls should also query the daemon */
61689d57b94SAlan Somers rqsr.r_len = length;
61789d57b94SAlan Somers rqsr.r_offset = offset;
61889d57b94SAlan Somers EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
619398c88c7SAlan Somers
620398c88c7SAlan Somers leak(fd);
621398c88c7SAlan Somers }
622398c88c7SAlan Somers
623398c88c7SAlan Somers /* EIO is not a permanent error, and may be retried */
TEST_F(PosixFallocate,eio)624398c88c7SAlan Somers TEST_F(PosixFallocate, eio)
625398c88c7SAlan Somers {
626398c88c7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
627398c88c7SAlan Somers const char RELPATH[] = "some_file.txt";
628398c88c7SAlan Somers uint64_t ino = 42;
629398c88c7SAlan Somers uint64_t offset = 0;
630398c88c7SAlan Somers uint64_t length = 1000;
631398c88c7SAlan Somers int fd;
632398c88c7SAlan Somers
633398c88c7SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
634398c88c7SAlan Somers expect_open(ino, 0, 1);
635398c88c7SAlan Somers expect_fallocate(ino, offset, length, 0, EIO);
636398c88c7SAlan Somers
637398c88c7SAlan Somers fd = open(FULLPATH, O_RDWR);
638398c88c7SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
639398c88c7SAlan Somers EXPECT_EQ(EIO, posix_fallocate(fd, offset, length));
640398c88c7SAlan Somers
641398c88c7SAlan Somers expect_fallocate(ino, offset, length, 0, 0);
642398c88c7SAlan Somers
643398c88c7SAlan Somers EXPECT_EQ(0, posix_fallocate(fd, offset, length));
644398c88c7SAlan Somers
645398c88c7SAlan Somers leak(fd);
646398c88c7SAlan Somers }
647398c88c7SAlan Somers
TEST_F(PosixFallocate,erofs)648398c88c7SAlan Somers TEST_F(PosixFallocate, erofs)
649398c88c7SAlan Somers {
650398c88c7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
651398c88c7SAlan Somers const char RELPATH[] = "some_file.txt";
652398c88c7SAlan Somers struct statfs statbuf;
653398c88c7SAlan Somers struct iovec *iov = NULL;
654398c88c7SAlan Somers int iovlen = 0;
655398c88c7SAlan Somers uint64_t ino = 42;
656398c88c7SAlan Somers uint64_t offset = 0;
657398c88c7SAlan Somers uint64_t length = 1000;
658398c88c7SAlan Somers int fd;
659398c88c7SAlan Somers int newflags;
660398c88c7SAlan Somers
661398c88c7SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
662398c88c7SAlan Somers expect_open(ino, 0, 1);
663398c88c7SAlan Somers EXPECT_CALL(*m_mock, process(
664398c88c7SAlan Somers ResultOf([](auto in) {
665398c88c7SAlan Somers return (in.header.opcode == FUSE_STATFS);
666398c88c7SAlan Somers }, Eq(true)),
667398c88c7SAlan Somers _)
668398c88c7SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out)
669398c88c7SAlan Somers {
670398c88c7SAlan Somers /*
671398c88c7SAlan Somers * All of the fields except f_flags are don't care, and f_flags
672398c88c7SAlan Somers * is set by the VFS
673398c88c7SAlan Somers */
674398c88c7SAlan Somers SET_OUT_HEADER_LEN(out, statfs);
675398c88c7SAlan Somers })));
676398c88c7SAlan Somers
677398c88c7SAlan Somers fd = open(FULLPATH, O_RDWR);
678398c88c7SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
679398c88c7SAlan Somers
680398c88c7SAlan Somers /* Remount read-only */
681398c88c7SAlan Somers ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
682398c88c7SAlan Somers newflags = statbuf.f_flags | MNT_UPDATE | MNT_RDONLY;
683398c88c7SAlan Somers build_iovec(&iov, &iovlen, "fstype", (void*)statbuf.f_fstypename, -1);
684398c88c7SAlan Somers build_iovec(&iov, &iovlen, "fspath", (void*)statbuf.f_mntonname, -1);
685398c88c7SAlan Somers build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
686398c88c7SAlan Somers ASSERT_EQ(0, nmount(iov, iovlen, newflags)) << strerror(errno);
68739f5d8ddSAlan Somers free_iovec(&iov, &iovlen);
688398c88c7SAlan Somers
689398c88c7SAlan Somers EXPECT_EQ(EROFS, posix_fallocate(fd, offset, length));
690398c88c7SAlan Somers
691398c88c7SAlan Somers leak(fd);
692398c88c7SAlan Somers }
693398c88c7SAlan Somers
TEST_F(PosixFallocate,ok)694398c88c7SAlan Somers TEST_F(PosixFallocate, ok)
695398c88c7SAlan Somers {
696398c88c7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
697398c88c7SAlan Somers const char RELPATH[] = "some_file.txt";
698398c88c7SAlan Somers struct stat sb0, sb1;
699398c88c7SAlan Somers uint64_t ino = 42;
700398c88c7SAlan Somers uint64_t offset = 0;
701398c88c7SAlan Somers uint64_t length = 1000;
702398c88c7SAlan Somers int fd;
703398c88c7SAlan Somers
704398c88c7SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
705398c88c7SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
706398c88c7SAlan Somers SET_OUT_HEADER_LEN(out, entry);
707398c88c7SAlan Somers out.body.entry.attr.mode = S_IFREG | 0644;
708398c88c7SAlan Somers out.body.entry.nodeid = ino;
709398c88c7SAlan Somers out.body.entry.entry_valid = UINT64_MAX;
710398c88c7SAlan Somers out.body.entry.attr_valid = UINT64_MAX;
711398c88c7SAlan Somers })));
712398c88c7SAlan Somers expect_open(ino, 0, 1);
713398c88c7SAlan Somers expect_fallocate(ino, offset, length, 0, 0);
714398c88c7SAlan Somers
715398c88c7SAlan Somers fd = open(FULLPATH, O_RDWR);
716398c88c7SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
717398c88c7SAlan Somers ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
718398c88c7SAlan Somers EXPECT_EQ(0, posix_fallocate(fd, offset, length));
719398c88c7SAlan Somers /*
720398c88c7SAlan Somers * Despite the originally cached file size of zero, stat should now
721398c88c7SAlan Somers * return either the new size or requery the daemon.
722398c88c7SAlan Somers */
723398c88c7SAlan Somers EXPECT_EQ(0, stat(FULLPATH, &sb1));
724398c88c7SAlan Somers EXPECT_EQ(length, (uint64_t)sb1.st_size);
725398c88c7SAlan Somers
726398c88c7SAlan Somers /* mtime and ctime should be updated */
727398c88c7SAlan Somers EXPECT_EQ(sb0.st_atime, sb1.st_atime);
728398c88c7SAlan Somers EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
729398c88c7SAlan Somers EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
730398c88c7SAlan Somers
731398c88c7SAlan Somers leak(fd);
732398c88c7SAlan Somers }
733398c88c7SAlan Somers
734398c88c7SAlan Somers /* fusefs should respect RLIMIT_FSIZE */
TEST_F(PosixFallocate,rlimit_fsize)735398c88c7SAlan Somers TEST_F(PosixFallocate, rlimit_fsize)
736398c88c7SAlan Somers {
737398c88c7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
738398c88c7SAlan Somers const char RELPATH[] = "some_file.txt";
739398c88c7SAlan Somers struct rlimit rl;
740398c88c7SAlan Somers uint64_t ino = 42;
741398c88c7SAlan Somers uint64_t offset = 0;
742398c88c7SAlan Somers uint64_t length = 1'000'000;
743398c88c7SAlan Somers int fd;
744398c88c7SAlan Somers
745398c88c7SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
746398c88c7SAlan Somers expect_open(ino, 0, 1);
747398c88c7SAlan Somers
748398c88c7SAlan Somers rl.rlim_cur = length / 2;
749398c88c7SAlan Somers rl.rlim_max = 10 * length;
750398c88c7SAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
751398c88c7SAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
752398c88c7SAlan Somers
753398c88c7SAlan Somers fd = open(FULLPATH, O_RDWR);
754398c88c7SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
755398c88c7SAlan Somers EXPECT_EQ(EFBIG, posix_fallocate(fd, offset, length));
756398c88c7SAlan Somers EXPECT_EQ(1, s_sigxfsz);
757398c88c7SAlan Somers
758398c88c7SAlan Somers leak(fd);
759398c88c7SAlan Somers }
760398c88c7SAlan Somers
761398c88c7SAlan Somers /* With older servers, no FUSE_FALLOCATE should be attempted */
TEST_F(PosixFallocate_7_18,einval)762398c88c7SAlan Somers TEST_F(PosixFallocate_7_18, einval)
763398c88c7SAlan Somers {
764398c88c7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
765398c88c7SAlan Somers const char RELPATH[] = "some_file.txt";
766398c88c7SAlan Somers uint64_t ino = 42;
767398c88c7SAlan Somers uint64_t offset = 0;
768398c88c7SAlan Somers uint64_t length = 1000;
769398c88c7SAlan Somers int fd;
770398c88c7SAlan Somers
771398c88c7SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
772398c88c7SAlan Somers expect_open(ino, 0, 1);
773398c88c7SAlan Somers
774398c88c7SAlan Somers fd = open(FULLPATH, O_RDWR);
775398c88c7SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
776398c88c7SAlan Somers EXPECT_EQ(EINVAL, posix_fallocate(fd, offset, length));
777398c88c7SAlan Somers
778398c88c7SAlan Somers leak(fd);
779398c88c7SAlan Somers }
780