2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
40 using namespace testing;
43 * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28.
44 * This bit was actually part of kernel protocol version 5.2, but never
45 * documented until after 7.28
47 #ifndef FUSE_FSYNC_FDATASYNC
48 #define FUSE_FSYNC_FDATASYNC 1
51 class Fsync: public FuseTest {
53 void expect_fsync(uint64_t ino, uint32_t flags, int error)
55 EXPECT_CALL(*m_mock, process(
56 ResultOf([=](auto in) {
57 return (in->header.opcode == FUSE_FSYNC &&
58 in->header.nodeid == ino &&
59 in->body.fsync.fh == FH &&
60 in->body.fsync.fsync_flags == flags);
63 ).WillOnce(Invoke(ReturnErrno(error)));
66 void expect_lookup(const char *relpath, uint64_t ino)
68 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
71 void expect_write(uint64_t ino, uint64_t size, const void *contents)
73 FuseTest::expect_write(ino, 0, size, size, 0, contents);
78 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
79 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */
80 TEST_F(Fsync, DISABLED_aio_fsync)
82 const char FULLPATH[] = "mountpoint/some_file.txt";
83 const char RELPATH[] = "some_file.txt";
84 const char *CONTENTS = "abcdefgh";
85 ssize_t bufsize = strlen(CONTENTS);
87 struct aiocb iocb, *piocb;
90 expect_lookup(RELPATH, ino);
91 expect_open(ino, 0, 1);
92 expect_getattr(ino, 0);
93 expect_write(ino, bufsize, CONTENTS);
94 expect_fsync(ino, 0, 0);
96 fd = open(FULLPATH, O_RDWR);
97 ASSERT_LE(0, fd) << strerror(errno);
98 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
100 bzero(&iocb, sizeof(iocb));
101 iocb.aio_fildes = fd;
103 ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno);
104 ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
106 /* Deliberately leak fd. close(2) will be tested in release.cc */
110 * fuse(4) should NOT fsync during VOP_RELEASE or VOP_INACTIVE
112 * This test only really make sense in writeback caching mode, but it should
113 * still pass in any cache mode.
117 const char FULLPATH[] = "mountpoint/some_file.txt";
118 const char RELPATH[] = "some_file.txt";
119 const char *CONTENTS = "abcdefgh";
120 ssize_t bufsize = strlen(CONTENTS);
124 expect_lookup(RELPATH, ino);
125 expect_open(ino, 0, 1);
126 expect_getattr(ino, 0);
127 expect_write(ino, bufsize, CONTENTS);
128 EXPECT_CALL(*m_mock, process(
129 ResultOf([=](auto in) {
130 return (in->header.opcode == FUSE_SETATTR);
133 ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
134 SET_OUT_HEADER_LEN(out, attr);
135 out->body.attr.attr.ino = ino; // Must match nodeid
137 EXPECT_CALL(*m_mock, process(
138 ResultOf([=](auto in) {
139 return (in->header.opcode == FUSE_FSYNC);
143 expect_release(ino, 1, 0, 0);
145 fd = open(FULLPATH, O_RDWR);
146 ASSERT_LE(0, fd) << strerror(errno);
147 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
153 const char FULLPATH[] = "mountpoint/some_file.txt";
154 const char RELPATH[] = "some_file.txt";
155 const char *CONTENTS = "abcdefgh";
156 ssize_t bufsize = strlen(CONTENTS);
160 expect_lookup(RELPATH, ino);
161 expect_open(ino, 0, 1);
162 expect_getattr(ino, 0);
163 expect_write(ino, bufsize, CONTENTS);
164 expect_fsync(ino, FUSE_FSYNC_FDATASYNC, EIO);
166 fd = open(FULLPATH, O_RDWR);
167 ASSERT_LE(0, fd) << strerror(errno);
168 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
169 ASSERT_NE(0, fdatasync(fd));
170 ASSERT_EQ(EIO, errno);
172 /* Deliberately leak fd. close(2) will be tested in release.cc */
176 * If the filesystem returns ENOSYS, it will be treated as success and
177 * subsequent calls to VOP_FSYNC will succeed automatically without being sent
178 * to the filesystem daemon
180 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */
181 TEST_F(Fsync, DISABLED_enosys)
183 const char FULLPATH[] = "mountpoint/some_file.txt";
184 const char RELPATH[] = "some_file.txt";
185 const char *CONTENTS = "abcdefgh";
186 ssize_t bufsize = strlen(CONTENTS);
190 expect_lookup(RELPATH, ino);
191 expect_open(ino, 0, 1);
192 expect_getattr(ino, 0);
193 expect_write(ino, bufsize, CONTENTS);
194 expect_fsync(ino, FUSE_FSYNC_FDATASYNC, ENOSYS);
196 fd = open(FULLPATH, O_RDWR);
197 ASSERT_LE(0, fd) << strerror(errno);
198 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
199 EXPECT_EQ(0, fdatasync(fd));
201 /* Subsequent calls shouldn't query the daemon*/
202 EXPECT_EQ(0, fdatasync(fd));
203 /* Deliberately leak fd. close(2) will be tested in release.cc */
207 TEST_F(Fsync, fdatasync)
209 const char FULLPATH[] = "mountpoint/some_file.txt";
210 const char RELPATH[] = "some_file.txt";
211 const char *CONTENTS = "abcdefgh";
212 ssize_t bufsize = strlen(CONTENTS);
216 expect_lookup(RELPATH, ino);
217 expect_open(ino, 0, 1);
218 expect_getattr(ino, 0);
219 expect_write(ino, bufsize, CONTENTS);
220 expect_fsync(ino, FUSE_FSYNC_FDATASYNC, 0);
222 fd = open(FULLPATH, O_RDWR);
223 ASSERT_LE(0, fd) << strerror(errno);
224 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
225 ASSERT_EQ(0, fdatasync(fd)) << strerror(errno);
227 /* Deliberately leak fd. close(2) will be tested in release.cc */
230 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */
231 TEST_F(Fsync, DISABLED_fsync)
233 const char FULLPATH[] = "mountpoint/some_file.txt";
234 const char RELPATH[] = "some_file.txt";
235 const char *CONTENTS = "abcdefgh";
236 ssize_t bufsize = strlen(CONTENTS);
240 expect_lookup(RELPATH, ino);
241 expect_open(ino, 0, 1);
242 expect_getattr(ino, 0);
243 expect_write(ino, bufsize, CONTENTS);
244 expect_fsync(ino, 0, 0);
246 fd = open(FULLPATH, O_RDWR);
247 ASSERT_LE(0, fd) << strerror(errno);
248 ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
249 ASSERT_EQ(0, fsync(fd)) << strerror(errno);
251 /* Deliberately leak fd. close(2) will be tested in release.cc */
254 /* Fsync should sync a file with dirty metadata but clean data */
255 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */
256 TEST_F(Fsync, DISABLED_fsync_metadata_only)
258 const char FULLPATH[] = "mountpoint/some_file.txt";
259 const char RELPATH[] = "some_file.txt";
264 expect_lookup(RELPATH, ino);
265 expect_open(ino, 0, 1);
266 expect_getattr(ino, 0);
267 EXPECT_CALL(*m_mock, process(
268 ResultOf([=](auto in) {
269 return (in->header.opcode == FUSE_SETATTR);
272 ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
273 SET_OUT_HEADER_LEN(out, attr);
274 out->body.attr.attr.ino = ino; // Must match nodeid
275 out->body.attr.attr.mode = S_IFREG | mode;
278 expect_fsync(ino, 0, 0);
280 fd = open(FULLPATH, O_RDWR);
281 ASSERT_LE(0, fd) << strerror(errno);
282 ASSERT_EQ(0, fchmod(fd, mode)) << strerror(errno);
283 ASSERT_EQ(0, fsync(fd)) << strerror(errno);
284 /* Deliberately leak fd. close(2) will be tested in release.cc */