xref: /freebsd/tests/sys/fs/fusefs/flush.cc (revision 9821f1d3231d4dea3e1319358f416425be2266a6)
1*9821f1d3SAlan Somers /*-
2*9821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*9821f1d3SAlan Somers  *
4*9821f1d3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
5*9821f1d3SAlan Somers  *
6*9821f1d3SAlan Somers  * This software was developed by BFF Storage Systems, LLC under sponsorship
7*9821f1d3SAlan Somers  * from the FreeBSD Foundation.
8*9821f1d3SAlan Somers  *
9*9821f1d3SAlan Somers  * Redistribution and use in source and binary forms, with or without
10*9821f1d3SAlan Somers  * modification, are permitted provided that the following conditions
11*9821f1d3SAlan Somers  * are met:
12*9821f1d3SAlan Somers  * 1. Redistributions of source code must retain the above copyright
13*9821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer.
14*9821f1d3SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
15*9821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
16*9821f1d3SAlan Somers  *    documentation and/or other materials provided with the distribution.
17*9821f1d3SAlan Somers  *
18*9821f1d3SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*9821f1d3SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*9821f1d3SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*9821f1d3SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*9821f1d3SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*9821f1d3SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*9821f1d3SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*9821f1d3SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*9821f1d3SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*9821f1d3SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*9821f1d3SAlan Somers  * SUCH DAMAGE.
29*9821f1d3SAlan Somers  */
30*9821f1d3SAlan Somers 
31*9821f1d3SAlan Somers extern "C" {
32*9821f1d3SAlan Somers #include <fcntl.h>
33*9821f1d3SAlan Somers #include <unistd.h>
34*9821f1d3SAlan Somers }
35*9821f1d3SAlan Somers 
36*9821f1d3SAlan Somers #include "mockfs.hh"
37*9821f1d3SAlan Somers #include "utils.hh"
38*9821f1d3SAlan Somers 
39*9821f1d3SAlan Somers using namespace testing;
40*9821f1d3SAlan Somers 
41*9821f1d3SAlan Somers class Flush: public FuseTest {
42*9821f1d3SAlan Somers 
43*9821f1d3SAlan Somers public:
44*9821f1d3SAlan Somers void expect_flush(uint64_t ino, int times, pid_t lo, ProcessMockerT r)
45*9821f1d3SAlan Somers {
46*9821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
47*9821f1d3SAlan Somers 		ResultOf([=](auto in) {
48*9821f1d3SAlan Somers 			return (in->header.opcode == FUSE_FLUSH &&
49*9821f1d3SAlan Somers 				in->header.nodeid == ino &&
50*9821f1d3SAlan Somers 				in->body.flush.lock_owner == (uint64_t)lo &&
51*9821f1d3SAlan Somers 				in->body.flush.fh == FH);
52*9821f1d3SAlan Somers 		}, Eq(true)),
53*9821f1d3SAlan Somers 		_)
54*9821f1d3SAlan Somers 	).Times(times)
55*9821f1d3SAlan Somers 	.WillRepeatedly(Invoke(r));
56*9821f1d3SAlan Somers }
57*9821f1d3SAlan Somers 
58*9821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino)
59*9821f1d3SAlan Somers {
60*9821f1d3SAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
61*9821f1d3SAlan Somers }
62*9821f1d3SAlan Somers 
63*9821f1d3SAlan Somers /*
64*9821f1d3SAlan Somers  * When testing FUSE_FLUSH, the FUSE_RELEASE calls are uninteresting.  This
65*9821f1d3SAlan Somers  * expectation will silence googlemock warnings
66*9821f1d3SAlan Somers  */
67*9821f1d3SAlan Somers void expect_release()
68*9821f1d3SAlan Somers {
69*9821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
70*9821f1d3SAlan Somers 		ResultOf([=](auto in) {
71*9821f1d3SAlan Somers 			return (in->header.opcode == FUSE_RELEASE);
72*9821f1d3SAlan Somers 		}, Eq(true)),
73*9821f1d3SAlan Somers 		_)
74*9821f1d3SAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(0)));
75*9821f1d3SAlan Somers }
76*9821f1d3SAlan Somers };
77*9821f1d3SAlan Somers 
78*9821f1d3SAlan Somers class FlushWithLocks: public Flush {
79*9821f1d3SAlan Somers 	virtual void SetUp() {
80*9821f1d3SAlan Somers 		m_init_flags = FUSE_POSIX_LOCKS;
81*9821f1d3SAlan Somers 		Flush::SetUp();
82*9821f1d3SAlan Somers 	}
83*9821f1d3SAlan Somers };
84*9821f1d3SAlan Somers 
85*9821f1d3SAlan Somers /* If a file descriptor is duplicated, every close causes FLUSH */
86*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */
87*9821f1d3SAlan Somers TEST_F(Flush, DISABLED_dup)
88*9821f1d3SAlan Somers {
89*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
90*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
91*9821f1d3SAlan Somers 	uint64_t ino = 42;
92*9821f1d3SAlan Somers 	int fd, fd2;
93*9821f1d3SAlan Somers 
94*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
95*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
96*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
97*9821f1d3SAlan Somers 	expect_flush(ino, 2, 0, ReturnErrno(0));
98*9821f1d3SAlan Somers 	expect_release();
99*9821f1d3SAlan Somers 
100*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
101*9821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
102*9821f1d3SAlan Somers 
103*9821f1d3SAlan Somers 	fd2 = dup(fd);
104*9821f1d3SAlan Somers 
105*9821f1d3SAlan Somers 	ASSERT_EQ(0, close(fd2)) << strerror(errno);
106*9821f1d3SAlan Somers 	ASSERT_EQ(0, close(fd)) << strerror(errno);
107*9821f1d3SAlan Somers }
108*9821f1d3SAlan Somers 
109*9821f1d3SAlan Somers /*
110*9821f1d3SAlan Somers  * Some FUSE filesystem cache data internally and flush it on release.  Such
111*9821f1d3SAlan Somers  * filesystems may generate errors during release.  On Linux, these get
112*9821f1d3SAlan Somers  * returned by close(2).  However, POSIX does not require close(2) to return
113*9821f1d3SAlan Somers  * this error.  FreeBSD's fuse(4) should return EIO if it returns an error at
114*9821f1d3SAlan Somers  * all.
115*9821f1d3SAlan Somers  */
116*9821f1d3SAlan Somers /* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */
117*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */
118*9821f1d3SAlan Somers TEST_F(Flush, DISABLED_eio)
119*9821f1d3SAlan Somers {
120*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
121*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
122*9821f1d3SAlan Somers 	uint64_t ino = 42;
123*9821f1d3SAlan Somers 	int fd;
124*9821f1d3SAlan Somers 
125*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
126*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
127*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
128*9821f1d3SAlan Somers 	expect_flush(ino, 1, 0, ReturnErrno(EIO));
129*9821f1d3SAlan Somers 	expect_release();
130*9821f1d3SAlan Somers 
131*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
132*9821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
133*9821f1d3SAlan Somers 
134*9821f1d3SAlan Somers 	ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno);
135*9821f1d3SAlan Somers }
136*9821f1d3SAlan Somers 
137*9821f1d3SAlan Somers /*
138*9821f1d3SAlan Somers  * If the filesystem returns ENOSYS, it will be treated as success and
139*9821f1d3SAlan Somers  * no more FUSE_FLUSH operations will be sent to the daemon
140*9821f1d3SAlan Somers  */
141*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */
142*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */
143*9821f1d3SAlan Somers TEST_F(Flush, DISABLED_enosys)
144*9821f1d3SAlan Somers {
145*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
146*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
147*9821f1d3SAlan Somers 	uint64_t ino = 42;
148*9821f1d3SAlan Somers 	int fd, fd2;
149*9821f1d3SAlan Somers 
150*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
151*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
152*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
153*9821f1d3SAlan Somers 	/* On the 2nd close, FUSE_FLUSH won't be sent at all */
154*9821f1d3SAlan Somers 	expect_flush(ino, 1, 0, ReturnErrno(ENOSYS));
155*9821f1d3SAlan Somers 	expect_release();
156*9821f1d3SAlan Somers 
157*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
158*9821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
159*9821f1d3SAlan Somers 
160*9821f1d3SAlan Somers 	fd2 = dup(fd);
161*9821f1d3SAlan Somers 
162*9821f1d3SAlan Somers 	EXPECT_EQ(0, close(fd2)) << strerror(errno);
163*9821f1d3SAlan Somers 	EXPECT_EQ(0, close(fd)) << strerror(errno);
164*9821f1d3SAlan Somers }
165*9821f1d3SAlan Somers 
166*9821f1d3SAlan Somers /* A FUSE_FLUSH should be sent on close(2) */
167*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */
168*9821f1d3SAlan Somers TEST_F(Flush, DISABLED_flush)
169*9821f1d3SAlan Somers {
170*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
171*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
172*9821f1d3SAlan Somers 	uint64_t ino = 42;
173*9821f1d3SAlan Somers 	int fd;
174*9821f1d3SAlan Somers 
175*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
176*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
177*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
178*9821f1d3SAlan Somers 	expect_flush(ino, 1, 0, ReturnErrno(0));
179*9821f1d3SAlan Somers 	expect_release();
180*9821f1d3SAlan Somers 
181*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
182*9821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
183*9821f1d3SAlan Somers 
184*9821f1d3SAlan Somers 	ASSERT_TRUE(0 == close(fd)) << strerror(errno);
185*9821f1d3SAlan Somers }
186*9821f1d3SAlan Somers 
187*9821f1d3SAlan Somers /*
188*9821f1d3SAlan Somers  * When closing a file with a POSIX file lock, flush should release the lock,
189*9821f1d3SAlan Somers  * _even_if_ it's not the process's last file descriptor for this file.
190*9821f1d3SAlan Somers  */
191*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */
192*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */
193*9821f1d3SAlan Somers TEST_F(FlushWithLocks, DISABLED_unlock_on_close)
194*9821f1d3SAlan Somers {
195*9821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
196*9821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
197*9821f1d3SAlan Somers 	uint64_t ino = 42;
198*9821f1d3SAlan Somers 	int fd, fd2;
199*9821f1d3SAlan Somers 	struct flock fl;
200*9821f1d3SAlan Somers 	pid_t pid = getpid();
201*9821f1d3SAlan Somers 
202*9821f1d3SAlan Somers 	expect_lookup(RELPATH, ino);
203*9821f1d3SAlan Somers 	expect_open(ino, 0, 1);
204*9821f1d3SAlan Somers 	expect_getattr(ino, 0);
205*9821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
206*9821f1d3SAlan Somers 		ResultOf([=](auto in) {
207*9821f1d3SAlan Somers 			return (in->header.opcode == FUSE_SETLK &&
208*9821f1d3SAlan Somers 				in->header.nodeid == ino &&
209*9821f1d3SAlan Somers 				in->body.setlk.fh == FH);
210*9821f1d3SAlan Somers 		}, Eq(true)),
211*9821f1d3SAlan Somers 		_)
212*9821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
213*9821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, setlk);
214*9821f1d3SAlan Somers 		out->body.setlk.lk = in->body.setlk.lk;
215*9821f1d3SAlan Somers 	})));
216*9821f1d3SAlan Somers 	expect_flush(ino, 1, pid, ReturnErrno(0));
217*9821f1d3SAlan Somers 
218*9821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
219*9821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
220*9821f1d3SAlan Somers 	fl.l_start = 0;
221*9821f1d3SAlan Somers 	fl.l_len = 0;
222*9821f1d3SAlan Somers 	fl.l_pid = pid;
223*9821f1d3SAlan Somers 	fl.l_type = F_RDLCK;
224*9821f1d3SAlan Somers 	fl.l_whence = SEEK_SET;
225*9821f1d3SAlan Somers 	fl.l_sysid = 0;
226*9821f1d3SAlan Somers 	ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
227*9821f1d3SAlan Somers 
228*9821f1d3SAlan Somers 	fd2 = dup(fd);
229*9821f1d3SAlan Somers 	ASSERT_EQ(0, close(fd2)) << strerror(errno);
230*9821f1d3SAlan Somers 	/* Deliberately leak fd */
231*9821f1d3SAlan Somers }
232