]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/mockfs.cc
Merge llvm trunk r366426, resolve conflicts, and update FREEBSD-Xlist.
[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  * $FreeBSD$
31  */
32
33 extern "C" {
34 #include <sys/param.h>
35
36 #include <sys/mount.h>
37 #include <sys/select.h>
38 #include <sys/stat.h>
39 #include <sys/uio.h>
40 #include <sys/user.h>
41
42 #include <fcntl.h>
43 #include <libutil.h>
44 #include <poll.h>
45 #include <pthread.h>
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49
50 #include "mntopts.h"    // for build_iovec
51 }
52
53 #include <cinttypes>
54
55 #include <gtest/gtest.h>
56
57 #include "mockfs.hh"
58
59 using namespace testing;
60
61 int verbosity = 0;
62
63 const char* opcode2opname(uint32_t opcode)
64 {
65         const int NUM_OPS = 39;
66         const char* table[NUM_OPS] = {
67                 "Unknown (opcode 0)",
68                 "LOOKUP",
69                 "FORGET",
70                 "GETATTR",
71                 "SETATTR",
72                 "READLINK",
73                 "SYMLINK",
74                 "Unknown (opcode 7)",
75                 "MKNOD",
76                 "MKDIR",
77                 "UNLINK",
78                 "RMDIR",
79                 "RENAME",
80                 "LINK",
81                 "OPEN",
82                 "READ",
83                 "WRITE",
84                 "STATFS",
85                 "RELEASE",
86                 "Unknown (opcode 19)",
87                 "FSYNC",
88                 "SETXATTR",
89                 "GETXATTR",
90                 "LISTXATTR",
91                 "REMOVEXATTR",
92                 "FLUSH",
93                 "INIT",
94                 "OPENDIR",
95                 "READDIR",
96                 "RELEASEDIR",
97                 "FSYNCDIR",
98                 "GETLK",
99                 "SETLK",
100                 "SETLKW",
101                 "ACCESS",
102                 "CREATE",
103                 "INTERRUPT",
104                 "BMAP",
105                 "DESTROY"
106         };
107         if (opcode >= NUM_OPS)
108                 return ("Unknown (opcode > max)");
109         else
110                 return (table[opcode]);
111 }
112
113 ProcessMockerT
114 ReturnErrno(int error)
115 {
116         return([=](auto in, auto &out) {
117                 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
118                 out0->header.unique = in.header.unique;
119                 out0->header.error = -error;
120                 out0->header.len = sizeof(out0->header);
121                 out.push_back(std::move(out0));
122         });
123 }
124
125 /* Helper function used for returning negative cache entries for LOOKUP */
126 ProcessMockerT
127 ReturnNegativeCache(const struct timespec *entry_valid)
128 {
129         return([=](auto in, auto &out) {
130                 /* nodeid means ENOENT and cache it */
131                 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
132                 out0->body.entry.nodeid = 0;
133                 out0->header.unique = in.header.unique;
134                 out0->header.error = 0;
135                 out0->body.entry.entry_valid = entry_valid->tv_sec;
136                 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
137                 SET_OUT_HEADER_LEN(*out0, entry);
138                 out.push_back(std::move(out0));
139         });
140 }
141
142 ProcessMockerT
143 ReturnImmediate(std::function<void(const mockfs_buf_in& in,
144                                    struct mockfs_buf_out &out)> f)
145 {
146         return([=](auto& in, auto &out) {
147                 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
148                 out0->header.unique = in.header.unique;
149                 f(in, *out0);
150                 out.push_back(std::move(out0));
151         });
152 }
153
154 void sigint_handler(int __unused sig) {
155         // Don't do anything except interrupt the daemon's read(2) call
156 }
157
158 void MockFS::debug_request(const mockfs_buf_in &in)
159 {
160         printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
161                 in.header.nodeid);
162         if (verbosity > 1) {
163                 printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u",
164                         in.header.uid, in.header.gid, in.header.pid,
165                         in.header.unique, in.header.len);
166         }
167         switch (in.header.opcode) {
168                 const char *name, *value;
169
170                 case FUSE_ACCESS:
171                         printf(" mask=%#x", in.body.access.mask);
172                         break;
173                 case FUSE_BMAP:
174                         printf(" block=%" PRIx64 " blocksize=%#x",
175                                 in.body.bmap.block, in.body.bmap.blocksize);
176                         break;
177                 case FUSE_CREATE:
178                         if (m_kernel_minor_version >= 12)
179                                 name = (const char*)in.body.bytes +
180                                         sizeof(fuse_create_in);
181                         else
182                                 name = (const char*)in.body.bytes +
183                                         sizeof(fuse_open_in);
184                         printf(" flags=%#x name=%s",
185                                 in.body.open.flags, name);
186                         break;
187                 case FUSE_FLUSH:
188                         printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
189                                 in.body.flush.fh,
190                                 in.body.flush.lock_owner);
191                         break;
192                 case FUSE_FORGET:
193                         printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
194                         break;
195                 case FUSE_FSYNC:
196                         printf(" flags=%#x", in.body.fsync.fsync_flags);
197                         break;
198                 case FUSE_FSYNCDIR:
199                         printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
200                         break;
201                 case FUSE_INTERRUPT:
202                         printf(" unique=%" PRIu64, in.body.interrupt.unique);
203                         break;
204                 case FUSE_LINK:
205                         printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
206                         break;
207                 case FUSE_LOOKUP:
208                         printf(" %s", in.body.lookup);
209                         break;
210                 case FUSE_MKDIR:
211                         name = (const char*)in.body.bytes +
212                                 sizeof(fuse_mkdir_in);
213                         printf(" name=%s mode=%#o umask=%#o", name,
214                                 in.body.mkdir.mode, in.body.mkdir.umask);
215                         break;
216                 case FUSE_MKNOD:
217                         if (m_kernel_minor_version >= 12)
218                                 name = (const char*)in.body.bytes +
219                                         sizeof(fuse_mknod_in);
220                         else
221                                 name = (const char*)in.body.bytes +
222                                         FUSE_COMPAT_MKNOD_IN_SIZE;
223                         printf(" mode=%#o rdev=%x umask=%#o name=%s",
224                                 in.body.mknod.mode, in.body.mknod.rdev,
225                                 in.body.mknod.umask, name);
226                         break;
227                 case FUSE_OPEN:
228                         printf(" flags=%#x", in.body.open.flags);
229                         break;
230                 case FUSE_OPENDIR:
231                         printf(" flags=%#x", in.body.opendir.flags);
232                         break;
233                 case FUSE_READ:
234                         printf(" offset=%" PRIu64 " size=%u",
235                                 in.body.read.offset,
236                                 in.body.read.size);
237                         if (verbosity > 1)
238                                 printf(" flags=%#x", in.body.read.flags);
239                         break;
240                 case FUSE_READDIR:
241                         printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
242                                 in.body.readdir.fh, in.body.readdir.offset,
243                                 in.body.readdir.size);
244                         break;
245                 case FUSE_RELEASE:
246                         printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
247                                 in.body.release.fh,
248                                 in.body.release.flags,
249                                 in.body.release.lock_owner);
250                         break;
251                 case FUSE_SETATTR:
252                         if (verbosity <= 1) {
253                                 printf(" valid=%#x", in.body.setattr.valid);
254                                 break;
255                         }
256                         if (in.body.setattr.valid & FATTR_MODE)
257                                 printf(" mode=%#o", in.body.setattr.mode);
258                         if (in.body.setattr.valid & FATTR_UID)
259                                 printf(" uid=%u", in.body.setattr.uid);
260                         if (in.body.setattr.valid & FATTR_GID)
261                                 printf(" gid=%u", in.body.setattr.gid);
262                         if (in.body.setattr.valid & FATTR_SIZE)
263                                 printf(" size=%" PRIu64, in.body.setattr.size);
264                         if (in.body.setattr.valid & FATTR_ATIME)
265                                 printf(" atime=%" PRIu64 ".%u",
266                                         in.body.setattr.atime,
267                                         in.body.setattr.atimensec);
268                         if (in.body.setattr.valid & FATTR_MTIME)
269                                 printf(" mtime=%" PRIu64 ".%u",
270                                         in.body.setattr.mtime,
271                                         in.body.setattr.mtimensec);
272                         if (in.body.setattr.valid & FATTR_FH)
273                                 printf(" fh=%" PRIu64 "", in.body.setattr.fh);
274                         break;
275                 case FUSE_SETLK:
276                         printf(" fh=%#" PRIx64 " owner=%" PRIu64
277                                 " type=%u pid=%u",
278                                 in.body.setlk.fh, in.body.setlk.owner,
279                                 in.body.setlk.lk.type,
280                                 in.body.setlk.lk.pid);
281                         if (verbosity >= 2) {
282                                 printf(" range=[%" PRIu64 "-%" PRIu64 "]",
283                                         in.body.setlk.lk.start,
284                                         in.body.setlk.lk.end);
285                         }
286                         break;
287                 case FUSE_SETXATTR:
288                         /* 
289                          * In theory neither the xattr name and value need be
290                          * ASCII, but in this test suite they always are.
291                          */
292                         name = (const char*)in.body.bytes +
293                                 sizeof(fuse_setxattr_in);
294                         value = name + strlen(name) + 1;
295                         printf(" %s=%s", name, value);
296                         break;
297                 case FUSE_WRITE:
298                         printf(" fh=%#" PRIx64 " offset=%" PRIu64
299                                 " size=%u write_flags=%u",
300                                 in.body.write.fh,
301                                 in.body.write.offset, in.body.write.size,
302                                 in.body.write.write_flags);
303                         if (verbosity > 1)
304                                 printf(" flags=%#x", in.body.write.flags);
305                         break;
306                 default:
307                         break;
308         }
309         printf("\n");
310 }
311
312 /* 
313  * Debug a FUSE response.
314  *
315  * This is mostly useful for asynchronous notifications, which don't correspond
316  * to any request
317  */
318 void MockFS::debug_response(const mockfs_buf_out &out) {
319         const char *name;
320
321         if (verbosity == 0)
322                 return;
323
324         switch (out.header.error) {
325                 case FUSE_NOTIFY_INVAL_ENTRY:
326                         name = (const char*)out.body.bytes +
327                                 sizeof(fuse_notify_inval_entry_out);
328                         printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
329                                 out.body.inval_entry.parent, name);
330                         break;
331                 case FUSE_NOTIFY_INVAL_INODE:
332                         printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
333                                 " len=%" PRIi64 "\n",
334                                 out.body.inval_inode.ino,
335                                 out.body.inval_inode.off,
336                                 out.body.inval_inode.len);
337                         break;
338                 case FUSE_NOTIFY_STORE:
339                         printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
340                                 " size=%" PRIu32 "\n",
341                                 out.body.store.nodeid,
342                                 out.body.store.offset,
343                                 out.body.store.size);
344                         break;
345                 default:
346                         break;
347         }
348 }
349
350 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
351         bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
352         uint32_t kernel_minor_version, uint32_t max_write, bool async,
353         bool noclusterr, unsigned time_gran, bool nointr)
354 {
355         struct sigaction sa;
356         struct iovec *iov = NULL;
357         int iovlen = 0;
358         char fdstr[15];
359         const bool trueval = true;
360
361         m_daemon_id = NULL;
362         m_kernel_minor_version = kernel_minor_version;
363         m_maxreadahead = max_readahead;
364         m_maxwrite = max_write;
365         m_nready = -1;
366         m_pm = pm;
367         m_time_gran = time_gran;
368         m_quit = false;
369         if (m_pm == KQ)
370                 m_kq = kqueue();
371         else
372                 m_kq = -1;
373
374         /*
375          * Kyua sets pwd to a testcase-unique tempdir; no need to use
376          * mkdtemp
377          */
378         /*
379          * googletest doesn't allow ASSERT_ in constructors, so we must throw
380          * instead.
381          */
382         if (mkdir("mountpoint" , 0755) && errno != EEXIST)
383                 throw(std::system_error(errno, std::system_category(),
384                         "Couldn't make mountpoint directory"));
385
386         switch (m_pm) {
387         case BLOCKING:
388                 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
389                 break;
390         default:
391                 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
392                 break;
393         }
394         if (m_fuse_fd < 0)
395                 throw(std::system_error(errno, std::system_category(),
396                         "Couldn't open /dev/fuse"));
397
398         m_pid = getpid();
399         m_child_pid = -1;
400
401         build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
402         build_iovec(&iov, &iovlen, "fspath",
403                     __DECONST(void *, "mountpoint"), -1);
404         build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
405         sprintf(fdstr, "%d", m_fuse_fd);
406         build_iovec(&iov, &iovlen, "fd", fdstr, -1);
407         if (allow_other) {
408                 build_iovec(&iov, &iovlen, "allow_other",
409                         __DECONST(void*, &trueval), sizeof(bool));
410         }
411         if (default_permissions) {
412                 build_iovec(&iov, &iovlen, "default_permissions",
413                         __DECONST(void*, &trueval), sizeof(bool));
414         }
415         if (push_symlinks_in) {
416                 build_iovec(&iov, &iovlen, "push_symlinks_in",
417                         __DECONST(void*, &trueval), sizeof(bool));
418         }
419         if (ro) {
420                 build_iovec(&iov, &iovlen, "ro",
421                         __DECONST(void*, &trueval), sizeof(bool));
422         }
423         if (async) {
424                 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
425                         sizeof(bool));
426         }
427         if (noclusterr) {
428                 build_iovec(&iov, &iovlen, "noclusterr",
429                         __DECONST(void*, &trueval), sizeof(bool));
430         }
431         if (nointr) {
432                 build_iovec(&iov, &iovlen, "nointr",
433                         __DECONST(void*, &trueval), sizeof(bool));
434         } else {
435                 build_iovec(&iov, &iovlen, "intr",
436                         __DECONST(void*, &trueval), sizeof(bool));
437         }
438         if (nmount(iov, iovlen, 0))
439                 throw(std::system_error(errno, std::system_category(),
440                         "Couldn't mount filesystem"));
441
442         // Setup default handler
443         ON_CALL(*this, process(_, _))
444                 .WillByDefault(Invoke(this, &MockFS::process_default));
445
446         init(flags);
447         bzero(&sa, sizeof(sa));
448         sa.sa_handler = sigint_handler;
449         sa.sa_flags = 0;        /* Don't set SA_RESTART! */
450         if (0 != sigaction(SIGUSR1, &sa, NULL))
451                 throw(std::system_error(errno, std::system_category(),
452                         "Couldn't handle SIGUSR1"));
453         if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
454                 throw(std::system_error(errno, std::system_category(),
455                         "Couldn't Couldn't start fuse thread"));
456 }
457
458 MockFS::~MockFS() {
459         kill_daemon();
460         if (m_daemon_id != NULL) {
461                 pthread_join(m_daemon_id, NULL);
462                 m_daemon_id = NULL;
463         }
464         ::unmount("mountpoint", MNT_FORCE);
465         rmdir("mountpoint");
466         if (m_kq >= 0)
467                 close(m_kq);
468 }
469
470 void MockFS::audit_request(const mockfs_buf_in &in) {
471         uint32_t inlen = in.header.len;
472         size_t fih = sizeof(in.header);
473         switch (in.header.opcode) {
474         case FUSE_LOOKUP:
475         case FUSE_RMDIR:
476         case FUSE_SYMLINK:
477         case FUSE_UNLINK:
478                 ASSERT_GT(inlen, fih) << "Missing request filename";
479                 break;
480         case FUSE_FORGET:
481                 ASSERT_EQ(inlen, fih + sizeof(in.body.forget));
482                 break;
483         case FUSE_GETATTR:
484                 ASSERT_EQ(inlen, fih + sizeof(in.body.getattr));
485                 break;
486         case FUSE_SETATTR:
487                 ASSERT_EQ(inlen, fih + sizeof(in.body.setattr));
488                 break;
489         case FUSE_READLINK:
490                 ASSERT_EQ(inlen, fih) << "Unexpected request body";
491                 break;
492         case FUSE_MKNOD:
493                 {
494                         size_t s;
495                         if (m_kernel_minor_version >= 12)
496                                 s = sizeof(in.body.mknod);
497                         else
498                                 s = FUSE_COMPAT_MKNOD_IN_SIZE;
499                         ASSERT_GE(inlen, fih + s) << "Missing request body";
500                         ASSERT_GT(inlen, fih + s) << "Missing request filename";
501                         break;
502                 }
503         case FUSE_MKDIR:
504                 ASSERT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
505                         "Missing request body";
506                 ASSERT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
507                         "Missing request filename";
508                 break;
509         case FUSE_RENAME:
510                 ASSERT_GE(inlen, fih + sizeof(in.body.rename)) <<
511                         "Missing request body";
512                 ASSERT_GT(inlen, fih + sizeof(in.body.rename)) <<
513                         "Missing request filename";
514                 break;
515         case FUSE_LINK:
516                 ASSERT_GE(inlen, fih + sizeof(in.body.link)) <<
517                         "Missing request body";
518                 ASSERT_GT(inlen, fih + sizeof(in.body.link)) <<
519                         "Missing request filename";
520                 break;
521         case FUSE_OPEN:
522                 ASSERT_EQ(inlen, fih + sizeof(in.body.open));
523                 break;
524         case FUSE_READ:
525                 ASSERT_EQ(inlen, fih + sizeof(in.body.read));
526                 break;
527         case FUSE_WRITE:
528                 {
529                         size_t s;
530
531                         if (m_kernel_minor_version >= 9)
532                                 s = sizeof(in.body.write);
533                         else
534                                 s = FUSE_COMPAT_WRITE_IN_SIZE;
535                         ASSERT_GE(inlen, fih + s) << "Missing request body";
536                         // I suppose a 0-byte write should be allowed
537                         break;
538                 }
539         case FUSE_DESTROY:
540         case FUSE_STATFS:
541                 ASSERT_EQ(inlen, fih);
542                 break;
543         case FUSE_RELEASE:
544                 ASSERT_EQ(inlen, fih + sizeof(in.body.release));
545                 break;
546         case FUSE_FSYNC:
547         case FUSE_FSYNCDIR:
548                 ASSERT_EQ(inlen, fih + sizeof(in.body.fsync));
549                 break;
550         case FUSE_SETXATTR:
551                 ASSERT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
552                         "Missing request body";
553                 ASSERT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
554                         "Missing request attribute name";
555                 break;
556         case FUSE_GETXATTR:
557                 ASSERT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
558                         "Missing request body";
559                 ASSERT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
560                         "Missing request attribute name";
561                 break;
562         case FUSE_LISTXATTR:
563                 ASSERT_EQ(inlen, fih + sizeof(in.body.listxattr));
564                 break;
565         case FUSE_REMOVEXATTR:
566                 ASSERT_GT(inlen, fih) << "Missing request attribute name";
567                 break;
568         case FUSE_FLUSH:
569                 ASSERT_EQ(inlen, fih + sizeof(in.body.flush));
570                 break;
571         case FUSE_INIT:
572                 ASSERT_EQ(inlen, fih + sizeof(in.body.init));
573                 break;
574         case FUSE_OPENDIR:
575                 ASSERT_EQ(inlen, fih + sizeof(in.body.opendir));
576                 break;
577         case FUSE_READDIR:
578                 ASSERT_EQ(inlen, fih + sizeof(in.body.readdir));
579                 break;
580         case FUSE_RELEASEDIR:
581                 ASSERT_EQ(inlen, fih + sizeof(in.body.releasedir));
582                 break;
583         case FUSE_GETLK:
584                 ASSERT_EQ(inlen, fih + sizeof(in.body.getlk));
585                 break;
586         case FUSE_SETLK:
587         case FUSE_SETLKW:
588                 ASSERT_EQ(inlen, fih + sizeof(in.body.setlk));
589                 break;
590         case FUSE_ACCESS:
591                 ASSERT_EQ(inlen, fih + sizeof(in.body.access));
592                 break;
593         case FUSE_CREATE:
594                 ASSERT_GE(inlen, fih + sizeof(in.body.create)) <<
595                         "Missing request body";
596                 ASSERT_GT(inlen, fih + sizeof(in.body.create)) <<
597                         "Missing request filename";
598                 break;
599         case FUSE_INTERRUPT:
600                 ASSERT_EQ(inlen, fih + sizeof(in.body.interrupt));
601                 break;
602         case FUSE_BMAP:
603                 ASSERT_EQ(inlen, fih + sizeof(in.body.bmap));
604                 break;
605         case FUSE_NOTIFY_REPLY:
606         case FUSE_BATCH_FORGET:
607         case FUSE_FALLOCATE:
608         case FUSE_IOCTL:
609         case FUSE_POLL:
610         case FUSE_READDIRPLUS:
611                 FAIL() << "Unsupported opcode?";
612         default:
613                 FAIL() << "Unknown opcode " << in.header.opcode;
614         }
615 }
616
617 void MockFS::init(uint32_t flags) {
618         std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
619         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
620
621         read_request(*in);
622         ASSERT_EQ(FUSE_INIT, in->header.opcode);
623
624         out->header.unique = in->header.unique;
625         out->header.error = 0;
626         out->body.init.major = FUSE_KERNEL_VERSION;
627         out->body.init.minor = m_kernel_minor_version;;
628         out->body.init.flags = in->body.init.flags & flags;
629         out->body.init.max_write = m_maxwrite;
630         out->body.init.max_readahead = m_maxreadahead;
631
632         if (m_kernel_minor_version < 23) {
633                 SET_OUT_HEADER_LEN(*out, init_7_22);
634         } else {
635                 out->body.init.time_gran = m_time_gran;
636                 SET_OUT_HEADER_LEN(*out, init);
637         }
638
639         write(m_fuse_fd, out.get(), out->header.len);
640 }
641
642 void MockFS::kill_daemon() {
643         m_quit = true;
644         if (m_daemon_id != NULL)
645                 pthread_kill(m_daemon_id, SIGUSR1);
646         // Closing the /dev/fuse file descriptor first allows unmount to
647         // succeed even if the daemon doesn't correctly respond to commands
648         // during the unmount sequence.
649         close(m_fuse_fd);
650         m_fuse_fd = -1;
651 }
652
653 void MockFS::loop() {
654         std::vector<std::unique_ptr<mockfs_buf_out>> out;
655
656         std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
657         ASSERT_TRUE(in != NULL);
658         while (!m_quit) {
659                 bzero(in.get(), sizeof(*in));
660                 read_request(*in);
661                 if (m_quit)
662                         break;
663                 if (verbosity > 0)
664                         debug_request(*in);
665                 audit_request(*in);
666                 if (pid_ok((pid_t)in->header.pid)) {
667                         process(*in, out);
668                 } else {
669                         /* 
670                          * Reject any requests from unknown processes.  Because
671                          * we actually do mount a filesystem, plenty of
672                          * unrelated system daemons may try to access it.
673                          */
674                         if (verbosity > 1)
675                                 printf("\tREJECTED (wrong pid %d)\n",
676                                         in->header.pid);
677                         process_default(*in, out);
678                 }
679                 for (auto &it: out)
680                         write_response(*it);
681                 out.clear();
682         }
683 }
684
685 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
686 {
687         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
688
689         out->header.unique = 0; /* 0 means asynchronous notification */
690         out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
691         out->body.inval_entry.parent = parent;
692         out->body.inval_entry.namelen = namelen;
693         strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
694                 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
695         out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
696                 namelen;
697         debug_response(*out);
698         write_response(*out);
699         return 0;
700 }
701
702 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
703 {
704         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
705
706         out->header.unique = 0; /* 0 means asynchronous notification */
707         out->header.error = FUSE_NOTIFY_INVAL_INODE;
708         out->body.inval_inode.ino = ino;
709         out->body.inval_inode.off = off;
710         out->body.inval_inode.len = len;
711         out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
712         debug_response(*out);
713         write_response(*out);
714         return 0;
715 }
716
717 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
718 {
719         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
720
721         out->header.unique = 0; /* 0 means asynchronous notification */
722         out->header.error = FUSE_NOTIFY_STORE;
723         out->body.store.nodeid = ino;
724         out->body.store.offset = off;
725         out->body.store.size = size;
726         bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
727         out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
728         debug_response(*out);
729         write_response(*out);
730         return 0;
731 }
732
733 bool MockFS::pid_ok(pid_t pid) {
734         if (pid == m_pid) {
735                 return (true);
736         } else if (pid == m_child_pid) {
737                 return (true);
738         } else {
739                 struct kinfo_proc *ki;
740                 bool ok = false;
741
742                 ki = kinfo_getproc(pid);
743                 if (ki == NULL)
744                         return (false);
745                 /* 
746                  * Allow access by the aio daemon processes so that our tests
747                  * can use aio functions
748                  */
749                 if (0 == strncmp("aiod", ki->ki_comm, 4))
750                         ok = true;
751                 free(ki);
752                 return (ok);
753         }
754 }
755
756 void MockFS::process_default(const mockfs_buf_in& in,
757                 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
758 {
759         std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
760         out0->header.unique = in.header.unique;
761         out0->header.error = -EOPNOTSUPP;
762         out0->header.len = sizeof(out0->header);
763         out.push_back(std::move(out0));
764 }
765
766 void MockFS::read_request(mockfs_buf_in &in) {
767         ssize_t res;
768         int nready = 0;
769         fd_set readfds;
770         pollfd fds[1];
771         struct kevent changes[1];
772         struct kevent events[1];
773         struct timespec timeout_ts;
774         struct timeval timeout_tv;
775         const int timeout_ms = 999;
776         int timeout_int, nfds;
777
778         switch (m_pm) {
779         case BLOCKING:
780                 break;
781         case KQ:
782                 timeout_ts.tv_sec = 0;
783                 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
784                 while (nready == 0) {
785                         EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
786                                 0, 0);
787                         nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
788                                 &timeout_ts);
789                         if (m_quit)
790                                 return;
791                 }
792                 ASSERT_LE(0, nready) << strerror(errno);
793                 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
794                 if (events[0].flags & EV_ERROR)
795                         FAIL() << strerror(events[0].data);
796                 else if (events[0].flags & EV_EOF)
797                         FAIL() << strerror(events[0].fflags);
798                 m_nready = events[0].data;
799                 break;
800         case POLL:
801                 timeout_int = timeout_ms;
802                 fds[0].fd = m_fuse_fd;
803                 fds[0].events = POLLIN;
804                 while (nready == 0) {
805                         nready = poll(fds, 1, timeout_int);
806                         if (m_quit)
807                                 return;
808                 }
809                 ASSERT_LE(0, nready) << strerror(errno);
810                 ASSERT_TRUE(fds[0].revents & POLLIN);
811                 break;
812         case SELECT:
813                 timeout_tv.tv_sec = 0;
814                 timeout_tv.tv_usec = timeout_ms * 1'000;
815                 nfds = m_fuse_fd + 1;
816                 while (nready == 0) {
817                         FD_ZERO(&readfds);
818                         FD_SET(m_fuse_fd, &readfds);
819                         nready = select(nfds, &readfds, NULL, NULL,
820                                 &timeout_tv);
821                         if (m_quit)
822                                 return;
823                 }
824                 ASSERT_LE(0, nready) << strerror(errno);
825                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
826                 break;
827         default:
828                 FAIL() << "not yet implemented";
829         }
830         res = read(m_fuse_fd, &in, sizeof(in));
831
832         if (res < 0 && !m_quit) {
833                 FAIL() << "read: " << strerror(errno);
834                 m_quit = true;
835         }
836         ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
837         /*
838          * Inconsistently, fuse_in_header.len is the size of the entire
839          * request,including header, even though fuse_out_header.len excludes
840          * the size of the header.
841          */
842         ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
843 }
844
845 void MockFS::write_response(const mockfs_buf_out &out) {
846         fd_set writefds;
847         pollfd fds[1];
848         int nready, nfds;
849         ssize_t r;
850
851         switch (m_pm) {
852         case BLOCKING:
853         case KQ:        /* EVFILT_WRITE is not supported */
854                 break;
855         case POLL:
856                 fds[0].fd = m_fuse_fd;
857                 fds[0].events = POLLOUT;
858                 nready = poll(fds, 1, INFTIM);
859                 ASSERT_LE(0, nready) << strerror(errno);
860                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
861                 ASSERT_TRUE(fds[0].revents & POLLOUT);
862                 break;
863         case SELECT:
864                 FD_ZERO(&writefds);
865                 FD_SET(m_fuse_fd, &writefds);
866                 nfds = m_fuse_fd + 1;
867                 nready = select(nfds, NULL, &writefds, NULL, NULL);
868                 ASSERT_LE(0, nready) << strerror(errno);
869                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
870                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
871                 break;
872         default:
873                 FAIL() << "not yet implemented";
874         }
875         r = write(m_fuse_fd, &out, out.header.len);
876         ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
877 }
878
879 void* MockFS::service(void *pthr_data) {
880         MockFS *mock_fs = (MockFS*)pthr_data;
881
882         mock_fs->loop();
883
884         return (NULL);
885 }
886
887 void MockFS::unmount() {
888         ::unmount("mountpoint", 0);
889 }