]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/fsync.cc
fusefs: VOP_FSYNC should be synchronous
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / fsync.cc
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 The FreeBSD Foundation
5  *
6  * This software was developed by BFF Storage Systems, LLC under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 extern "C" {
32 #include <aio.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 }
36
37 #include "mockfs.hh"
38 #include "utils.hh"
39
40 using namespace testing;
41
42 /*
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
46  */
47 #ifndef FUSE_FSYNC_FDATASYNC
48 #define FUSE_FSYNC_FDATASYNC 1
49 #endif
50
51 class Fsync: public FuseTest {
52 public:
53 void expect_fsync(uint64_t ino, uint32_t flags, int error)
54 {
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);
61                 }, Eq(true)),
62                 _)
63         ).WillOnce(Invoke(ReturnErrno(error)));
64 }
65
66 void expect_lookup(const char *relpath, uint64_t ino)
67 {
68         FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
69 }
70
71 void expect_write(uint64_t ino, uint64_t size, const void *contents)
72 {
73         FuseTest::expect_write(ino, 0, size, size, 0, contents);
74 }
75
76 };
77
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)
81 {
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);
86         uint64_t ino = 42;
87         struct aiocb iocb, *piocb;
88         int fd;
89
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);
95
96         fd = open(FULLPATH, O_RDWR);
97         ASSERT_LE(0, fd) << strerror(errno);
98         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
99
100         bzero(&iocb, sizeof(iocb));
101         iocb.aio_fildes = fd;
102
103         ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno);
104         ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
105
106         /* Deliberately leak fd.  close(2) will be tested in release.cc */
107 }
108
109 /*
110  * fuse(4) should NOT fsync during VOP_RELEASE or VOP_INACTIVE
111  *
112  * This test only really make sense in writeback caching mode, but it should
113  * still pass in any cache mode.
114  */
115 TEST_F(Fsync, close)
116 {
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);
121         uint64_t ino = 42;
122         int fd;
123
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);
131                 }, Eq(true)),
132                 _)
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
136         })));
137         EXPECT_CALL(*m_mock, process(
138                 ResultOf([=](auto in) {
139                         return (in->header.opcode == FUSE_FSYNC);
140                 }, Eq(true)),
141                 _)
142         ).Times(0);
143         expect_release(ino, 1, 0, 0);
144
145         fd = open(FULLPATH, O_RDWR);
146         ASSERT_LE(0, fd) << strerror(errno);
147         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
148         close(fd);
149 }
150
151 TEST_F(Fsync, eio)
152 {
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);
157         uint64_t ino = 42;
158         int fd;
159
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);
165
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);
171
172         /* Deliberately leak fd.  close(2) will be tested in release.cc */
173 }
174
175 /*
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
179  */
180 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */
181 TEST_F(Fsync, DISABLED_enosys)
182 {
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);
187         uint64_t ino = 42;
188         int fd;
189
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);
195
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));
200
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 */
204 }
205
206
207 TEST_F(Fsync, fdatasync)
208 {
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);
213         uint64_t ino = 42;
214         int fd;
215
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);
221
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);
226
227         /* Deliberately leak fd.  close(2) will be tested in release.cc */
228 }
229
230 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */
231 TEST_F(Fsync, DISABLED_fsync)
232 {
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);
237         uint64_t ino = 42;
238         int fd;
239
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);
245
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);
250
251         /* Deliberately leak fd.  close(2) will be tested in release.cc */
252 }
253
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)
257 {
258         const char FULLPATH[] = "mountpoint/some_file.txt";
259         const char RELPATH[] = "some_file.txt";
260         uint64_t ino = 42;
261         int fd;
262         mode_t mode = 0755;
263
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);
270                 }, Eq(true)),
271                 _)
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;
276         })));
277
278         expect_fsync(ino, 0, 0);
279
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 */
285 }