]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/mockfs.cc
fusefs: implement VOP_ADVLOCK
[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_ACCESS:
166                         printf(" mask=%#x", in->body.access.mask);
167                         break;
168                 case FUSE_CREATE:
169                         name = (const char*)in->body.bytes +
170                                 sizeof(fuse_open_in);
171                         printf(" flags=%#x name=%s",
172                                 in->body.open.flags, name);
173                         break;
174                 case FUSE_FLUSH:
175                         printf(" fh=%#lx lock_owner=%lu", in->body.flush.fh,
176                                 in->body.flush.lock_owner);
177                         break;
178                 case FUSE_FORGET:
179                         printf(" nlookup=%lu", in->body.forget.nlookup);
180                         break;
181                 case FUSE_FSYNC:
182                         printf(" flags=%#x", in->body.fsync.fsync_flags);
183                         break;
184                 case FUSE_FSYNCDIR:
185                         printf(" flags=%#x", in->body.fsyncdir.fsync_flags);
186                         break;
187                 case FUSE_LOOKUP:
188                         printf(" %s", in->body.lookup);
189                         break;
190                 case FUSE_MKNOD:
191                         printf(" mode=%#o rdev=%x", in->body.mknod.mode,
192                                 in->body.mknod.rdev);
193                         break;
194                 case FUSE_OPEN:
195                         printf(" flags=%#x mode=%#o",
196                                 in->body.open.flags, in->body.open.mode);
197                         break;
198                 case FUSE_OPENDIR:
199                         printf(" flags=%#x mode=%#o",
200                                 in->body.opendir.flags, in->body.opendir.mode);
201                         break;
202                 case FUSE_READ:
203                         printf(" offset=%lu size=%u", in->body.read.offset,
204                                 in->body.read.size);
205                         break;
206                 case FUSE_READDIR:
207                         printf(" fh=%#lx offset=%lu size=%u",
208                                 in->body.readdir.fh, in->body.readdir.offset,
209                                 in->body.readdir.size);
210                         break;
211                 case FUSE_RELEASE:
212                         printf(" fh=%#lx flags=%#x lock_owner=%lu",
213                                 in->body.release.fh,
214                                 in->body.release.flags,
215                                 in->body.release.lock_owner);
216                         break;
217                 case FUSE_SETATTR:
218                         if (verbosity <= 1) {
219                                 printf(" valid=%#x", in->body.setattr.valid);
220                                 break;
221                         }
222                         if (in->body.setattr.valid & FATTR_MODE)
223                                 printf(" mode=%#o", in->body.setattr.mode);
224                         if (in->body.setattr.valid & FATTR_UID)
225                                 printf(" uid=%u", in->body.setattr.uid);
226                         if (in->body.setattr.valid & FATTR_GID)
227                                 printf(" gid=%u", in->body.setattr.gid);
228                         if (in->body.setattr.valid & FATTR_SIZE)
229                                 printf(" size=%zu", in->body.setattr.size);
230                         if (in->body.setattr.valid & FATTR_ATIME)
231                                 printf(" atime=%zu.%u",
232                                         in->body.setattr.atime,
233                                         in->body.setattr.atimensec);
234                         if (in->body.setattr.valid & FATTR_MTIME)
235                                 printf(" mtime=%zu.%u",
236                                         in->body.setattr.mtime,
237                                         in->body.setattr.mtimensec);
238                         if (in->body.setattr.valid & FATTR_FH)
239                                 printf(" fh=%zu", in->body.setattr.fh);
240                         break;
241                 case FUSE_SETLK:
242                         printf(" fh=%#lx owner=%lu type=%u pid=%u",
243                                 in->body.setlk.fh, in->body.setlk.owner,
244                                 in->body.setlk.lk.type,
245                                 in->body.setlk.lk.pid);
246                         if (verbosity >= 2) {
247                                 printf(" range=[%lu-%lu]",
248                                         in->body.setlk.lk.start,
249                                         in->body.setlk.lk.end);
250                         }
251                         break;
252                 case FUSE_SETXATTR:
253                         /* 
254                          * In theory neither the xattr name and value need be
255                          * ASCII, but in this test suite they always are.
256                          */
257                         name = (const char*)in->body.bytes +
258                                 sizeof(fuse_setxattr_in);
259                         value = name + strlen(name) + 1;
260                         printf(" %s=%s", name, value);
261                         break;
262                 case FUSE_WRITE:
263                         printf(" fh=%#lx offset=%lu size=%u flags=%u",
264                                 in->body.write.fh,
265                                 in->body.write.offset, in->body.write.size,
266                                 in->body.write.write_flags);
267                         break;
268                 default:
269                         break;
270         }
271         printf("\n");
272 }
273
274 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
275         bool push_symlinks_in, bool ro, uint32_t flags)
276 {
277         struct iovec *iov = NULL;
278         int iovlen = 0;
279         char fdstr[15];
280         const bool trueval = true;
281
282         m_daemon_id = NULL;
283         m_maxreadahead = max_readahead;
284         quit = 0;
285
286         /*
287          * Kyua sets pwd to a testcase-unique tempdir; no need to use
288          * mkdtemp
289          */
290         /*
291          * googletest doesn't allow ASSERT_ in constructors, so we must throw
292          * instead.
293          */
294         if (mkdir("mountpoint" , 0755) && errno != EEXIST)
295                 throw(std::system_error(errno, std::system_category(),
296                         "Couldn't make mountpoint directory"));
297
298         m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
299         if (m_fuse_fd < 0)
300                 throw(std::system_error(errno, std::system_category(),
301                         "Couldn't open /dev/fuse"));
302         sprintf(fdstr, "%d", m_fuse_fd);
303
304         m_pid = getpid();
305         m_child_pid = -1;
306
307         build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
308         build_iovec(&iov, &iovlen, "fspath",
309                     __DECONST(void *, "mountpoint"), -1);
310         build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
311         build_iovec(&iov, &iovlen, "fd", fdstr, -1);
312         if (allow_other) {
313                 build_iovec(&iov, &iovlen, "allow_other",
314                         __DECONST(void*, &trueval), sizeof(bool));
315         }
316         if (default_permissions) {
317                 build_iovec(&iov, &iovlen, "default_permissions",
318                         __DECONST(void*, &trueval), sizeof(bool));
319         }
320         if (push_symlinks_in) {
321                 build_iovec(&iov, &iovlen, "push_symlinks_in",
322                         __DECONST(void*, &trueval), sizeof(bool));
323         }
324         if (ro) {
325                 build_iovec(&iov, &iovlen, "ro",
326                         __DECONST(void*, &trueval), sizeof(bool));
327         }
328         if (nmount(iov, iovlen, 0))
329                 throw(std::system_error(errno, std::system_category(),
330                         "Couldn't mount filesystem"));
331
332         // Setup default handler
333         ON_CALL(*this, process(_, _))
334                 .WillByDefault(Invoke(this, &MockFS::process_default));
335
336         init(flags);
337         signal(SIGUSR1, sigint_handler);
338         if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
339                 throw(std::system_error(errno, std::system_category(),
340                         "Couldn't Couldn't start fuse thread"));
341 }
342
343 MockFS::~MockFS() {
344         kill_daemon();
345         ::unmount("mountpoint", MNT_FORCE);
346         if (m_daemon_id != NULL) {
347                 pthread_join(m_daemon_id, NULL);
348                 m_daemon_id = NULL;
349         }
350         rmdir("mountpoint");
351 }
352
353 void MockFS::init(uint32_t flags) {
354         mockfs_buf_in *in;
355         mockfs_buf_out *out;
356
357         in = (mockfs_buf_in*) malloc(sizeof(*in));
358         ASSERT_TRUE(in != NULL);
359         out = (mockfs_buf_out*) malloc(sizeof(*out));
360         ASSERT_TRUE(out != NULL);
361
362         read_request(in);
363         ASSERT_EQ(FUSE_INIT, in->header.opcode);
364
365         memset(out, 0, sizeof(*out));
366         out->header.unique = in->header.unique;
367         out->header.error = 0;
368         out->body.init.major = FUSE_KERNEL_VERSION;
369         out->body.init.minor = FUSE_KERNEL_MINOR_VERSION;
370         out->body.init.flags = in->body.init.flags & flags;
371
372         /*
373          * The default max_write is set to this formula in libfuse, though
374          * individual filesystems can lower it.  The "- 4096" was added in
375          * commit 154ffe2, with the commit message "fix".
376          */
377         uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096;
378         /* For testing purposes, it should be distinct from MAXPHYS */
379         m_max_write = MIN(default_max_write, MAXPHYS / 2);
380         out->body.init.max_write = m_max_write;
381
382         out->body.init.max_readahead = m_maxreadahead;
383         SET_OUT_HEADER_LEN(out, init);
384         write(m_fuse_fd, out, out->header.len);
385
386         free(in);
387 }
388
389 void MockFS::kill_daemon() {
390         if (m_daemon_id != NULL) {
391                 pthread_kill(m_daemon_id, SIGUSR1);
392                 // Closing the /dev/fuse file descriptor first allows unmount
393                 // to succeed even if the daemon doesn't correctly respond to
394                 // commands during the unmount sequence.
395                 close(m_fuse_fd);
396         }
397 }
398
399 void MockFS::loop() {
400         mockfs_buf_in *in;
401         std::vector<mockfs_buf_out*> out;
402
403         in = (mockfs_buf_in*) malloc(sizeof(*in));
404         ASSERT_TRUE(in != NULL);
405         while (!quit) {
406                 bzero(in, sizeof(*in));
407                 read_request(in);
408                 if (quit)
409                         break;
410                 if (verbosity > 0)
411                         debug_fuseop(in);
412                 if (pid_ok((pid_t)in->header.pid)) {
413                         process(in, out);
414                 } else {
415                         /* 
416                          * Reject any requests from unknown processes.  Because
417                          * we actually do mount a filesystem, plenty of
418                          * unrelated system daemons may try to access it.
419                          */
420                         process_default(in, out);
421                 }
422                 for (auto &it: out) {
423                         ASSERT_TRUE(write(m_fuse_fd, it, it->header.len) > 0 ||
424                                     errno == EAGAIN)
425                                 << strerror(errno);
426                         delete it;
427                 }
428                 out.clear();
429         }
430         free(in);
431 }
432
433 bool MockFS::pid_ok(pid_t pid) {
434         if (pid == m_pid) {
435                 return (true);
436         } else if (pid == m_child_pid) {
437                 return (true);
438         } else {
439                 struct kinfo_proc *ki;
440                 bool ok = false;
441
442                 ki = kinfo_getproc(pid);
443                 if (ki == NULL)
444                         return (false);
445                 /* 
446                  * Allow access by the aio daemon processes so that our tests
447                  * can use aio functions
448                  */
449                 if (0 == strncmp("aiod", ki->ki_comm, 4))
450                         ok = true;
451                 free(ki);
452                 return (ok);
453         }
454 }
455
456 void MockFS::process_default(const mockfs_buf_in *in,
457                 std::vector<mockfs_buf_out*> &out)
458 {
459         auto out0 = new mockfs_buf_out;
460         out0->header.unique = in->header.unique;
461         out0->header.error = -EOPNOTSUPP;
462         out0->header.len = sizeof(out0->header);
463         out.push_back(out0);
464 }
465
466 void MockFS::read_request(mockfs_buf_in *in) {
467         ssize_t res;
468
469         res = read(m_fuse_fd, in, sizeof(*in));
470         if (res < 0 && !quit)
471                 perror("read");
472         ASSERT_TRUE(res >= (ssize_t)sizeof(in->header) || quit);
473 }
474
475 void* MockFS::service(void *pthr_data) {
476         MockFS *mock_fs = (MockFS*)pthr_data;
477
478         mock_fs->loop();
479
480         return (NULL);
481 }
482
483 void MockFS::unmount() {
484         ::unmount("mountpoint", 0);
485 }