]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/mockfs.cc
fusefs: send FUSE_FLUSH during VOP_CLOSE
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / mockfs.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 <sys/param.h>
33
34 #include <sys/mount.h>
35 #include <sys/stat.h>
36 #include <sys/uio.h>
37 #include <sys/user.h>
38
39 #include <fcntl.h>
40 #include <libutil.h>
41 #include <pthread.h>
42 #include <signal.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45
46 #include "mntopts.h"    // for build_iovec
47 }
48
49 #include <gtest/gtest.h>
50
51 #include "mockfs.hh"
52
53 using namespace testing;
54
55 int verbosity = 0;
56 static sig_atomic_t quit = 0;
57
58 const char* opcode2opname(uint32_t opcode)
59 {
60         const int NUM_OPS = 39;
61         const char* table[NUM_OPS] = {
62                 "Unknown (opcode 0)",
63                 "LOOKUP",
64                 "FORGET",
65                 "GETATTR",
66                 "SETATTR",
67                 "READLINK",
68                 "SYMLINK",
69                 "Unknown (opcode 7)",
70                 "MKNOD",
71                 "MKDIR",
72                 "UNLINK",
73                 "RMDIR",
74                 "RENAME",
75                 "LINK",
76                 "OPEN",
77                 "READ",
78                 "WRITE",
79                 "STATFS",
80                 "RELEASE",
81                 "Unknown (opcode 19)",
82                 "FSYNC",
83                 "SETXATTR",
84                 "GETXATTR",
85                 "LISTXATTR",
86                 "REMOVEXATTR",
87                 "FLUSH",
88                 "INIT",
89                 "OPENDIR",
90                 "READDIR",
91                 "RELEASEDIR",
92                 "FSYNCDIR",
93                 "GETLK",
94                 "SETLK",
95                 "SETLKW",
96                 "ACCESS",
97                 "CREATE",
98                 "INTERRUPT",
99                 "BMAP",
100                 "DESTROY"
101         };
102         if (opcode >= NUM_OPS)
103                 return ("Unknown (opcode > max)");
104         else
105                 return (table[opcode]);
106 }
107
108 ProcessMockerT
109 ReturnErrno(int error)
110 {
111         return([=](auto in, auto &out) {
112                 auto out0 = new mockfs_buf_out;
113                 out0->header.unique = in->header.unique;
114                 out0->header.error = -error;
115                 out0->header.len = sizeof(out0->header);
116                 out.push_back(out0);
117         });
118 }
119
120 /* Helper function used for returning negative cache entries for LOOKUP */
121 ProcessMockerT
122 ReturnNegativeCache(const struct timespec *entry_valid)
123 {
124         return([=](auto in, auto &out) {
125                 /* nodeid means ENOENT and cache it */
126                 auto out0 = new mockfs_buf_out;
127                 out0->body.entry.nodeid = 0;
128                 out0->header.unique = in->header.unique;
129                 out0->header.error = 0;
130                 out0->body.entry.entry_valid = entry_valid->tv_sec;
131                 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
132                 SET_OUT_HEADER_LEN(out0, entry);
133                 out.push_back(out0);
134         });
135 }
136
137 ProcessMockerT
138 ReturnImmediate(std::function<void(const struct mockfs_buf_in *in,
139                                    struct mockfs_buf_out *out)> f)
140 {
141         return([=](auto in, auto &out) {
142                 auto out0 = new mockfs_buf_out;
143                 out0->header.unique = in->header.unique;
144                 f(in, out0);
145                 out.push_back(out0);
146         });
147 }
148
149 void sigint_handler(int __unused sig) {
150         quit = 1;
151 }
152
153 void debug_fuseop(const mockfs_buf_in *in)
154 {
155         printf("%-11s ino=%2lu", opcode2opname(in->header.opcode),
156                 in->header.nodeid);
157         if (verbosity > 1) {
158                 printf(" uid=%5u gid=%5u pid=%5u unique=%lu len=%u",
159                         in->header.uid, in->header.gid, in->header.pid,
160                         in->header.unique, in->header.len);
161         }
162         switch (in->header.opcode) {
163                 const char *name, *value;
164
165                 case FUSE_CREATE:
166                         name = (const char*)in->body.bytes +
167                                 sizeof(fuse_open_in);
168                         printf(" flags=%#x name=%s",
169                                 in->body.open.flags, name);
170                         break;
171                 case FUSE_FLUSH:
172                         printf(" fh=%#lx lock_owner=%lu", in->body.flush.fh,
173                                 in->body.flush.lock_owner);
174                         break;
175                 case FUSE_FORGET:
176                         printf(" nlookup=%lu", in->body.forget.nlookup);
177                         break;
178                 case FUSE_FSYNC:
179                         printf(" flags=%#x", in->body.fsync.fsync_flags);
180                         break;
181                 case FUSE_FSYNCDIR:
182                         printf(" flags=%#x", in->body.fsyncdir.fsync_flags);
183                         break;
184                 case FUSE_LOOKUP:
185                         printf(" %s", in->body.lookup);
186                         break;
187                 case FUSE_MKNOD:
188                         printf(" mode=%#o rdev=%x", in->body.mknod.mode,
189                                 in->body.mknod.rdev);
190                         break;
191                 case FUSE_OPEN:
192                         printf(" flags=%#x mode=%#o",
193                                 in->body.open.flags, in->body.open.mode);
194                         break;
195                 case FUSE_OPENDIR:
196                         printf(" flags=%#x mode=%#o",
197                                 in->body.opendir.flags, in->body.opendir.mode);
198                         break;
199                 case FUSE_READ:
200                         printf(" offset=%lu size=%u", in->body.read.offset,
201                                 in->body.read.size);
202                         break;
203                 case FUSE_READDIR:
204                         printf(" fh=%#lx offset=%lu size=%u",
205                                 in->body.readdir.fh, in->body.readdir.offset,
206                                 in->body.readdir.size);
207                         break;
208                 case FUSE_RELEASE:
209                         printf(" fh=%#lx flags=%#x lock_owner=%lu",
210                                 in->body.release.fh,
211                                 in->body.release.flags,
212                                 in->body.release.lock_owner);
213                         break;
214                 case FUSE_SETATTR:
215                         if (verbosity <= 1) {
216                                 printf(" valid=%#x", in->body.setattr.valid);
217                                 break;
218                         }
219                         if (in->body.setattr.valid & FATTR_MODE)
220                                 printf(" mode=%#o", in->body.setattr.mode);
221                         if (in->body.setattr.valid & FATTR_UID)
222                                 printf(" uid=%u", in->body.setattr.uid);
223                         if (in->body.setattr.valid & FATTR_GID)
224                                 printf(" gid=%u", in->body.setattr.gid);
225                         if (in->body.setattr.valid & FATTR_SIZE)
226                                 printf(" size=%zu", in->body.setattr.size);
227                         if (in->body.setattr.valid & FATTR_ATIME)
228                                 printf(" atime=%zu.%u",
229                                         in->body.setattr.atime,
230                                         in->body.setattr.atimensec);
231                         if (in->body.setattr.valid & FATTR_MTIME)
232                                 printf(" mtime=%zu.%u",
233                                         in->body.setattr.mtime,
234                                         in->body.setattr.mtimensec);
235                         if (in->body.setattr.valid & FATTR_FH)
236                                 printf(" fh=%zu", in->body.setattr.fh);
237                         break;
238                 case FUSE_SETXATTR:
239                         /* 
240                          * In theory neither the xattr name and value need be
241                          * ASCII, but in this test suite they always are.
242                          */
243                         name = (const char*)in->body.bytes +
244                                 sizeof(fuse_setxattr_in);
245                         value = name + strlen(name) + 1;
246                         printf(" %s=%s", name, value);
247                         break;
248                 case FUSE_WRITE:
249                         printf(" offset=%lu size=%u flags=%u",
250                                 in->body.write.offset, in->body.write.size,
251                                 in->body.write.write_flags);
252                         break;
253                 default:
254                         break;
255         }
256         printf("\n");
257 }
258
259 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
260         bool push_symlinks_in, uint32_t flags)
261 {
262         struct iovec *iov = NULL;
263         int iovlen = 0;
264         char fdstr[15];
265         const bool trueval = true;
266
267         m_daemon_id = NULL;
268         m_maxreadahead = max_readahead;
269         quit = 0;
270
271         /*
272          * Kyua sets pwd to a testcase-unique tempdir; no need to use
273          * mkdtemp
274          */
275         /*
276          * googletest doesn't allow ASSERT_ in constructors, so we must throw
277          * instead.
278          */
279         if (mkdir("mountpoint" , 0755) && errno != EEXIST)
280                 throw(std::system_error(errno, std::system_category(),
281                         "Couldn't make mountpoint directory"));
282
283         m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
284         if (m_fuse_fd < 0)
285                 throw(std::system_error(errno, std::system_category(),
286                         "Couldn't open /dev/fuse"));
287         sprintf(fdstr, "%d", m_fuse_fd);
288
289         m_pid = getpid();
290         m_child_pid = -1;
291
292         build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
293         build_iovec(&iov, &iovlen, "fspath",
294                     __DECONST(void *, "mountpoint"), -1);
295         build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
296         build_iovec(&iov, &iovlen, "fd", fdstr, -1);
297         if (allow_other) {
298                 build_iovec(&iov, &iovlen, "allow_other",
299                         __DECONST(void*, &trueval), sizeof(bool));
300         }
301         if (default_permissions) {
302                 build_iovec(&iov, &iovlen, "default_permissions",
303                         __DECONST(void*, &trueval), sizeof(bool));
304         }
305         if (push_symlinks_in) {
306                 build_iovec(&iov, &iovlen, "push_symlinks_in",
307                         __DECONST(void*, &trueval), sizeof(bool));
308         }
309         if (nmount(iov, iovlen, 0))
310                 throw(std::system_error(errno, std::system_category(),
311                         "Couldn't mount filesystem"));
312
313         // Setup default handler
314         ON_CALL(*this, process(_, _))
315                 .WillByDefault(Invoke(this, &MockFS::process_default));
316
317         init(flags);
318         signal(SIGUSR1, sigint_handler);
319         if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
320                 throw(std::system_error(errno, std::system_category(),
321                         "Couldn't Couldn't start fuse thread"));
322 }
323
324 MockFS::~MockFS() {
325         kill_daemon();
326         ::unmount("mountpoint", MNT_FORCE);
327         if (m_daemon_id != NULL) {
328                 pthread_join(m_daemon_id, NULL);
329                 m_daemon_id = NULL;
330         }
331         rmdir("mountpoint");
332 }
333
334 void MockFS::init(uint32_t flags) {
335         mockfs_buf_in *in;
336         mockfs_buf_out *out;
337
338         in = (mockfs_buf_in*) malloc(sizeof(*in));
339         ASSERT_TRUE(in != NULL);
340         out = (mockfs_buf_out*) malloc(sizeof(*out));
341         ASSERT_TRUE(out != NULL);
342
343         read_request(in);
344         ASSERT_EQ(FUSE_INIT, in->header.opcode);
345
346         memset(out, 0, sizeof(*out));
347         out->header.unique = in->header.unique;
348         out->header.error = 0;
349         out->body.init.major = FUSE_KERNEL_VERSION;
350         out->body.init.minor = FUSE_KERNEL_MINOR_VERSION;
351         out->body.init.flags = in->body.init.flags & flags;
352
353         /*
354          * The default max_write is set to this formula in libfuse, though
355          * individual filesystems can lower it.  The "- 4096" was added in
356          * commit 154ffe2, with the commit message "fix".
357          */
358         uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096;
359         /* For testing purposes, it should be distinct from MAXPHYS */
360         m_max_write = MIN(default_max_write, MAXPHYS / 2);
361         out->body.init.max_write = m_max_write;
362
363         out->body.init.max_readahead = m_maxreadahead;
364         SET_OUT_HEADER_LEN(out, init);
365         write(m_fuse_fd, out, out->header.len);
366
367         free(in);
368 }
369
370 void MockFS::kill_daemon() {
371         if (m_daemon_id != NULL) {
372                 pthread_kill(m_daemon_id, SIGUSR1);
373                 // Closing the /dev/fuse file descriptor first allows unmount
374                 // to succeed even if the daemon doesn't correctly respond to
375                 // commands during the unmount sequence.
376                 close(m_fuse_fd);
377         }
378 }
379
380 void MockFS::loop() {
381         mockfs_buf_in *in;
382         std::vector<mockfs_buf_out*> out;
383
384         in = (mockfs_buf_in*) malloc(sizeof(*in));
385         ASSERT_TRUE(in != NULL);
386         while (!quit) {
387                 bzero(in, sizeof(*in));
388                 read_request(in);
389                 if (quit)
390                         break;
391                 if (verbosity > 0)
392                         debug_fuseop(in);
393                 if (pid_ok((pid_t)in->header.pid)) {
394                         process(in, out);
395                 } else {
396                         /* 
397                          * Reject any requests from unknown processes.  Because
398                          * we actually do mount a filesystem, plenty of
399                          * unrelated system daemons may try to access it.
400                          */
401                         process_default(in, out);
402                 }
403                 for (auto &it: out) {
404                         ASSERT_TRUE(write(m_fuse_fd, it, it->header.len) > 0 ||
405                                     errno == EAGAIN)
406                                 << strerror(errno);
407                         delete it;
408                 }
409                 out.clear();
410         }
411         free(in);
412 }
413
414 bool MockFS::pid_ok(pid_t pid) {
415         if (pid == m_pid) {
416                 return (true);
417         } else if (pid == m_child_pid) {
418                 return (true);
419         } else {
420                 struct kinfo_proc *ki;
421                 bool ok = false;
422
423                 ki = kinfo_getproc(pid);
424                 if (ki == NULL)
425                         return (false);
426                 /* 
427                  * Allow access by the aio daemon processes so that our tests
428                  * can use aio functions
429                  */
430                 if (0 == strncmp("aiod", ki->ki_comm, 4))
431                         ok = true;
432                 free(ki);
433                 return (ok);
434         }
435 }
436
437 void MockFS::process_default(const mockfs_buf_in *in,
438                 std::vector<mockfs_buf_out*> &out)
439 {
440         auto out0 = new mockfs_buf_out;
441         out0->header.unique = in->header.unique;
442         out0->header.error = -EOPNOTSUPP;
443         out0->header.len = sizeof(out0->header);
444         out.push_back(out0);
445 }
446
447 void MockFS::read_request(mockfs_buf_in *in) {
448         ssize_t res;
449
450         res = read(m_fuse_fd, in, sizeof(*in));
451         if (res < 0 && !quit)
452                 perror("read");
453         ASSERT_TRUE(res >= (ssize_t)sizeof(in->header) || quit);
454 }
455
456 void* MockFS::service(void *pthr_data) {
457         MockFS *mock_fs = (MockFS*)pthr_data;
458
459         mock_fs->loop();
460
461         return (NULL);
462 }
463
464 void MockFS::unmount() {
465         ::unmount("mountpoint", 0);
466 }