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
34 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/select.h>
50 #include "mntopts.h" // for build_iovec
55 #include <gtest/gtest.h>
59 using namespace testing;
63 const char* opcode2opname(uint32_t opcode)
65 const char* table[] = {
85 "Unknown (opcode 19)",
115 if (opcode >= nitems(table))
116 return ("Unknown (opcode > max)");
118 return (table[opcode]);
122 ReturnErrno(int error)
124 return([=](auto in, auto &out) {
125 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
126 out0->header.unique = in.header.unique;
127 out0->header.error = -error;
128 out0->header.len = sizeof(out0->header);
129 out.push_back(std::move(out0));
133 /* Helper function used for returning negative cache entries for LOOKUP */
135 ReturnNegativeCache(const struct timespec *entry_valid)
137 return([=](auto in, auto &out) {
138 /* nodeid means ENOENT and cache it */
139 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
140 out0->body.entry.nodeid = 0;
141 out0->header.unique = in.header.unique;
142 out0->header.error = 0;
143 out0->body.entry.entry_valid = entry_valid->tv_sec;
144 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
145 SET_OUT_HEADER_LEN(*out0, entry);
146 out.push_back(std::move(out0));
151 ReturnImmediate(std::function<void(const mockfs_buf_in& in,
152 struct mockfs_buf_out &out)> f)
154 return([=](auto& in, auto &out) {
155 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
156 out0->header.unique = in.header.unique;
158 out.push_back(std::move(out0));
162 void sigint_handler(int __unused sig) {
163 // Don't do anything except interrupt the daemon's read(2) call
166 void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
168 printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
171 printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
173 in.header.uid, in.header.gid, in.header.pid,
174 in.header.unique, in.header.len, buflen);
176 switch (in.header.opcode) {
177 const char *name, *value;
180 printf(" mask=%#x", in.body.access.mask);
183 printf(" block=%" PRIx64 " blocksize=%#x",
184 in.body.bmap.block, in.body.bmap.blocksize);
186 case FUSE_COPY_FILE_RANGE:
187 printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
188 " off_out=%" PRIu64 " size=%" PRIu64,
189 in.body.copy_file_range.off_in,
190 in.body.copy_file_range.nodeid_out,
191 in.body.copy_file_range.off_out,
192 in.body.copy_file_range.len);
194 printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
196 in.body.copy_file_range.fh_in,
197 in.body.copy_file_range.fh_out,
198 in.body.copy_file_range.flags);
201 if (m_kernel_minor_version >= 12)
202 name = (const char*)in.body.bytes +
203 sizeof(fuse_create_in);
205 name = (const char*)in.body.bytes +
206 sizeof(fuse_open_in);
207 printf(" flags=%#x name=%s",
208 in.body.open.flags, name);
211 printf(" fh=%#" PRIx64 " offset=%" PRIu64
212 " length=%" PRIx64 " mode=%#x",
213 in.body.fallocate.fh,
214 in.body.fallocate.offset,
215 in.body.fallocate.length,
216 in.body.fallocate.mode);
219 printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
221 in.body.flush.lock_owner);
224 printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
227 printf(" flags=%#x", in.body.fsync.fsync_flags);
230 printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
233 printf(" fh=%#" PRIx64
236 in.body.getlk.lk.type,
237 in.body.getlk.lk.pid);
238 if (verbosity >= 2) {
239 printf(" range=[%" PRIi64 ":%" PRIi64 "]",
240 in.body.getlk.lk.start,
241 in.body.getlk.lk.end);
245 printf(" unique=%" PRIu64, in.body.interrupt.unique);
248 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
251 printf(" size=%" PRIu32, in.body.listxattr.size);
254 printf(" %s", in.body.lookup);
257 switch (in.body.lseek.whence) {
259 printf(" SEEK_HOLE offset=%jd",
260 in.body.lseek.offset);
263 printf(" SEEK_DATA offset=%jd",
264 in.body.lseek.offset);
267 printf(" whence=%u offset=%jd",
268 in.body.lseek.whence, in.body.lseek.offset);
273 name = (const char*)in.body.bytes +
274 sizeof(fuse_mkdir_in);
275 printf(" name=%s mode=%#o umask=%#o", name,
276 in.body.mkdir.mode, in.body.mkdir.umask);
279 if (m_kernel_minor_version >= 12)
280 name = (const char*)in.body.bytes +
281 sizeof(fuse_mknod_in);
283 name = (const char*)in.body.bytes +
284 FUSE_COMPAT_MKNOD_IN_SIZE;
285 printf(" mode=%#o rdev=%x umask=%#o name=%s",
286 in.body.mknod.mode, in.body.mknod.rdev,
287 in.body.mknod.umask, name);
290 printf(" flags=%#x", in.body.open.flags);
293 printf(" flags=%#x", in.body.opendir.flags);
296 printf(" offset=%" PRIu64 " size=%u",
300 printf(" flags=%#x", in.body.read.flags);
303 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
304 in.body.readdir.fh, in.body.readdir.offset,
305 in.body.readdir.size);
308 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
310 in.body.release.flags,
311 in.body.release.lock_owner);
315 const char *src = (const char*)in.body.bytes +
316 sizeof(fuse_rename_in);
317 const char *dst = src + strlen(src) + 1;
318 printf(" src=%s newdir=%" PRIu64 " dst=%s",
319 src, in.body.rename.newdir, dst);
323 if (verbosity <= 1) {
324 printf(" valid=%#x", in.body.setattr.valid);
327 if (in.body.setattr.valid & FATTR_MODE)
328 printf(" mode=%#o", in.body.setattr.mode);
329 if (in.body.setattr.valid & FATTR_UID)
330 printf(" uid=%u", in.body.setattr.uid);
331 if (in.body.setattr.valid & FATTR_GID)
332 printf(" gid=%u", in.body.setattr.gid);
333 if (in.body.setattr.valid & FATTR_SIZE)
334 printf(" size=%" PRIu64, in.body.setattr.size);
335 if (in.body.setattr.valid & FATTR_ATIME)
336 printf(" atime=%" PRIu64 ".%u",
337 in.body.setattr.atime,
338 in.body.setattr.atimensec);
339 if (in.body.setattr.valid & FATTR_MTIME)
340 printf(" mtime=%" PRIu64 ".%u",
341 in.body.setattr.mtime,
342 in.body.setattr.mtimensec);
343 if (in.body.setattr.valid & FATTR_FH)
344 printf(" fh=%" PRIu64 "", in.body.setattr.fh);
347 printf(" fh=%#" PRIx64 " owner=%" PRIu64
349 in.body.setlk.fh, in.body.setlk.owner,
350 in.body.setlk.lk.type,
351 in.body.setlk.lk.pid);
352 if (verbosity >= 2) {
353 printf(" range=[%" PRIi64 ":%" PRIi64 "]",
354 in.body.setlk.lk.start,
355 in.body.setlk.lk.end);
360 * In theory neither the xattr name and value need be
361 * ASCII, but in this test suite they always are.
363 name = (const char*)in.body.bytes +
364 sizeof(fuse_setxattr_in);
365 value = name + strlen(name) + 1;
366 printf(" %s=%s", name, value);
369 printf(" fh=%#" PRIx64 " offset=%" PRIu64
370 " size=%u write_flags=%u",
372 in.body.write.offset, in.body.write.size,
373 in.body.write.write_flags);
375 printf(" flags=%#x", in.body.write.flags);
384 * Debug a FUSE response.
386 * This is mostly useful for asynchronous notifications, which don't correspond
389 void MockFS::debug_response(const mockfs_buf_out &out) {
395 switch (out.header.error) {
396 case FUSE_NOTIFY_INVAL_ENTRY:
397 name = (const char*)out.body.bytes +
398 sizeof(fuse_notify_inval_entry_out);
399 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
400 out.body.inval_entry.parent, name);
402 case FUSE_NOTIFY_INVAL_INODE:
403 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
404 " len=%" PRIi64 "\n",
405 out.body.inval_inode.ino,
406 out.body.inval_inode.off,
407 out.body.inval_inode.len);
409 case FUSE_NOTIFY_STORE:
410 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
411 " size=%" PRIu32 "\n",
412 out.body.store.nodeid,
413 out.body.store.offset,
414 out.body.store.size);
421 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
422 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
423 uint32_t kernel_minor_version, uint32_t max_write, bool async,
424 bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
425 const char *fsname, const char *subtype)
428 struct iovec *iov = NULL;
431 const bool trueval = true;
434 m_expected_write_errno = 0;
435 m_kernel_minor_version = kernel_minor_version;
436 m_maxreadahead = max_readahead;
437 m_maxwrite = MIN(max_write, max_max_write);
440 m_time_gran = time_gran;
449 * Kyua sets pwd to a testcase-unique tempdir; no need to use
453 * googletest doesn't allow ASSERT_ in constructors, so we must throw
456 if (mkdir("mountpoint" , 0755) && errno != EEXIST)
457 throw(std::system_error(errno, std::system_category(),
458 "Couldn't make mountpoint directory"));
462 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
465 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
469 throw(std::system_error(errno, std::system_category(),
470 "Couldn't open /dev/fuse"));
475 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
476 build_iovec(&iov, &iovlen, "fspath",
477 __DECONST(void *, "mountpoint"), -1);
478 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
479 sprintf(fdstr, "%d", m_fuse_fd);
480 build_iovec(&iov, &iovlen, "fd", fdstr, -1);
482 build_iovec(&iov, &iovlen, "allow_other",
483 __DECONST(void*, &trueval), sizeof(bool));
485 if (default_permissions) {
486 build_iovec(&iov, &iovlen, "default_permissions",
487 __DECONST(void*, &trueval), sizeof(bool));
489 if (push_symlinks_in) {
490 build_iovec(&iov, &iovlen, "push_symlinks_in",
491 __DECONST(void*, &trueval), sizeof(bool));
494 build_iovec(&iov, &iovlen, "ro",
495 __DECONST(void*, &trueval), sizeof(bool));
498 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
502 build_iovec(&iov, &iovlen, "noatime",
503 __DECONST(void*, &trueval), sizeof(bool));
506 build_iovec(&iov, &iovlen, "noclusterr",
507 __DECONST(void*, &trueval), sizeof(bool));
510 build_iovec(&iov, &iovlen, "nointr",
511 __DECONST(void*, &trueval), sizeof(bool));
513 build_iovec(&iov, &iovlen, "intr",
514 __DECONST(void*, &trueval), sizeof(bool));
517 build_iovec(&iov, &iovlen, "fsname=",
518 __DECONST(void*, fsname), -1);
521 build_iovec(&iov, &iovlen, "subtype=",
522 __DECONST(void*, subtype), -1);
524 if (nmount(iov, iovlen, 0))
525 throw(std::system_error(errno, std::system_category(),
526 "Couldn't mount filesystem"));
528 // Setup default handler
529 ON_CALL(*this, process(_, _))
530 .WillByDefault(Invoke(this, &MockFS::process_default));
533 bzero(&sa, sizeof(sa));
534 sa.sa_handler = sigint_handler;
535 sa.sa_flags = 0; /* Don't set SA_RESTART! */
536 if (0 != sigaction(SIGUSR1, &sa, NULL))
537 throw(std::system_error(errno, std::system_category(),
538 "Couldn't handle SIGUSR1"));
539 if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
540 throw(std::system_error(errno, std::system_category(),
541 "Couldn't Couldn't start fuse thread"));
546 if (m_daemon_id != NULL) {
547 pthread_join(m_daemon_id, NULL);
550 ::unmount("mountpoint", MNT_FORCE);
556 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
557 uint32_t inlen = in.header.len;
558 size_t fih = sizeof(in.header);
559 switch (in.header.opcode) {
564 EXPECT_GT(inlen, fih) << "Missing request filename";
565 // No redundant information for checking buflen
568 EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
569 EXPECT_EQ((size_t)buflen, inlen);
572 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
573 EXPECT_EQ((size_t)buflen, inlen);
576 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
577 EXPECT_EQ((size_t)buflen, inlen);
580 EXPECT_EQ(inlen, fih) << "Unexpected request body";
581 EXPECT_EQ((size_t)buflen, inlen);
586 if (m_kernel_minor_version >= 12)
587 s = sizeof(in.body.mknod);
589 s = FUSE_COMPAT_MKNOD_IN_SIZE;
590 EXPECT_GE(inlen, fih + s) << "Missing request body";
591 EXPECT_GT(inlen, fih + s) << "Missing request filename";
592 // No redundant information for checking buflen
596 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
597 "Missing request body";
598 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
599 "Missing request filename";
600 // No redundant information for checking buflen
603 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
604 "Missing request body";
605 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
606 "Missing request filename";
607 // No redundant information for checking buflen
610 EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
611 "Missing request body";
612 EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
613 "Missing request filename";
614 // No redundant information for checking buflen
617 EXPECT_EQ(inlen, fih + sizeof(in.body.open));
618 EXPECT_EQ((size_t)buflen, inlen);
621 EXPECT_EQ(inlen, fih + sizeof(in.body.read));
622 EXPECT_EQ((size_t)buflen, inlen);
628 if (m_kernel_minor_version >= 9)
629 s = sizeof(in.body.write);
631 s = FUSE_COMPAT_WRITE_IN_SIZE;
632 // I suppose a 0-byte write should be allowed
633 EXPECT_GE(inlen, fih + s) << "Missing request body";
634 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
639 EXPECT_EQ(inlen, fih);
640 EXPECT_EQ((size_t)buflen, inlen);
643 EXPECT_EQ(inlen, fih + sizeof(in.body.release));
644 EXPECT_EQ((size_t)buflen, inlen);
648 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
649 EXPECT_EQ((size_t)buflen, inlen);
652 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
653 "Missing request body";
654 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
655 "Missing request attribute name";
656 // No redundant information for checking buflen
659 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
660 "Missing request body";
661 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
662 "Missing request attribute name";
663 // No redundant information for checking buflen
666 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
667 EXPECT_EQ((size_t)buflen, inlen);
669 case FUSE_REMOVEXATTR:
670 EXPECT_GT(inlen, fih) << "Missing request attribute name";
671 // No redundant information for checking buflen
674 EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
675 EXPECT_EQ((size_t)buflen, inlen);
678 EXPECT_EQ(inlen, fih + sizeof(in.body.init));
679 EXPECT_EQ((size_t)buflen, inlen);
682 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
683 EXPECT_EQ((size_t)buflen, inlen);
686 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
687 EXPECT_EQ((size_t)buflen, inlen);
689 case FUSE_RELEASEDIR:
690 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
691 EXPECT_EQ((size_t)buflen, inlen);
694 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
695 EXPECT_EQ((size_t)buflen, inlen);
699 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
700 EXPECT_EQ((size_t)buflen, inlen);
703 EXPECT_EQ(inlen, fih + sizeof(in.body.access));
704 EXPECT_EQ((size_t)buflen, inlen);
707 EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
708 "Missing request body";
709 EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
710 "Missing request filename";
711 // No redundant information for checking buflen
714 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
715 EXPECT_EQ((size_t)buflen, inlen);
718 EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
719 EXPECT_EQ((size_t)buflen, inlen);
722 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
723 EXPECT_EQ((size_t)buflen, inlen);
726 EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
727 EXPECT_EQ((size_t)buflen, inlen);
729 case FUSE_COPY_FILE_RANGE:
730 EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
731 EXPECT_EQ(0ul, in.body.copy_file_range.flags);
732 EXPECT_EQ((size_t)buflen, inlen);
734 case FUSE_NOTIFY_REPLY:
735 case FUSE_BATCH_FORGET:
738 case FUSE_READDIRPLUS:
739 FAIL() << "Unsupported opcode?";
741 FAIL() << "Unknown opcode " << in.header.opcode;
744 * Check that the ticket's unique value is sequential. Technically it
745 * doesn't need to be sequential, merely unique. But the current
746 * fusefs driver _does_ make it sequential, and that's easy to check
749 if (in.header.unique != ++m_last_unique)
750 FAIL() << "Non-sequential unique value";
753 void MockFS::init(uint32_t flags) {
756 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
757 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
759 read_request(*in, buflen);
761 debug_request(*in, buflen);
762 audit_request(*in, buflen);
763 ASSERT_EQ(FUSE_INIT, in->header.opcode);
765 out->header.unique = in->header.unique;
766 out->header.error = 0;
767 out->body.init.major = FUSE_KERNEL_VERSION;
768 out->body.init.minor = m_kernel_minor_version;;
769 out->body.init.flags = in->body.init.flags & flags;
770 out->body.init.max_write = m_maxwrite;
771 out->body.init.max_readahead = m_maxreadahead;
773 if (m_kernel_minor_version < 23) {
774 SET_OUT_HEADER_LEN(*out, init_7_22);
776 out->body.init.time_gran = m_time_gran;
777 SET_OUT_HEADER_LEN(*out, init);
780 write(m_fuse_fd, out.get(), out->header.len);
783 void MockFS::kill_daemon() {
785 if (m_daemon_id != NULL)
786 pthread_kill(m_daemon_id, SIGUSR1);
787 // Closing the /dev/fuse file descriptor first allows unmount to
788 // succeed even if the daemon doesn't correctly respond to commands
789 // during the unmount sequence.
794 void MockFS::loop() {
795 std::vector<std::unique_ptr<mockfs_buf_out>> out;
797 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
798 ASSERT_TRUE(in != NULL);
802 bzero(in.get(), sizeof(*in));
803 read_request(*in, buflen);
804 m_expected_write_errno = 0;
808 debug_request(*in, buflen);
809 audit_request(*in, buflen);
810 if (pid_ok((pid_t)in->header.pid)) {
814 * Reject any requests from unknown processes. Because
815 * we actually do mount a filesystem, plenty of
816 * unrelated system daemons may try to access it.
819 printf("\tREJECTED (wrong pid %d)\n",
821 process_default(*in, out);
829 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
831 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
833 out->header.unique = 0; /* 0 means asynchronous notification */
834 out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
835 out->body.inval_entry.parent = parent;
836 out->body.inval_entry.namelen = namelen;
837 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
838 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
839 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
841 debug_response(*out);
842 write_response(*out);
846 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
848 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
850 out->header.unique = 0; /* 0 means asynchronous notification */
851 out->header.error = FUSE_NOTIFY_INVAL_INODE;
852 out->body.inval_inode.ino = ino;
853 out->body.inval_inode.off = off;
854 out->body.inval_inode.len = len;
855 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
856 debug_response(*out);
857 write_response(*out);
861 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
863 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
865 out->header.unique = 0; /* 0 means asynchronous notification */
866 out->header.error = FUSE_NOTIFY_STORE;
867 out->body.store.nodeid = ino;
868 out->body.store.offset = off;
869 out->body.store.size = size;
870 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
871 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
872 debug_response(*out);
873 write_response(*out);
877 bool MockFS::pid_ok(pid_t pid) {
880 } else if (pid == m_child_pid) {
883 struct kinfo_proc *ki;
886 ki = kinfo_getproc(pid);
890 * Allow access by the aio daemon processes so that our tests
891 * can use aio functions
893 if (0 == strncmp("aiod", ki->ki_comm, 4))
900 void MockFS::process_default(const mockfs_buf_in& in,
901 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
903 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
904 out0->header.unique = in.header.unique;
905 out0->header.error = -EOPNOTSUPP;
906 out0->header.len = sizeof(out0->header);
907 out.push_back(std::move(out0));
910 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
914 struct kevent changes[1];
915 struct kevent events[1];
916 struct timespec timeout_ts;
917 struct timeval timeout_tv;
918 const int timeout_ms = 999;
919 int timeout_int, nfds;
926 timeout_ts.tv_sec = 0;
927 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
928 while (nready == 0) {
929 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
930 EV_ADD | EV_ONESHOT, 0, 0, 0);
931 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
936 ASSERT_LE(0, nready) << strerror(errno);
937 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
938 if (events[0].flags & EV_ERROR)
939 FAIL() << strerror(events[0].data);
940 else if (events[0].flags & EV_EOF)
941 FAIL() << strerror(events[0].fflags);
942 m_nready = events[0].data;
945 timeout_int = timeout_ms;
946 fds[0].fd = m_fuse_fd;
947 fds[0].events = POLLIN;
948 while (nready == 0) {
949 nready = poll(fds, 1, timeout_int);
953 ASSERT_LE(0, nready) << strerror(errno);
954 ASSERT_TRUE(fds[0].revents & POLLIN);
960 timeout_tv.tv_sec = 0;
961 timeout_tv.tv_usec = timeout_ms * 1'000;
963 while (nready == 0) {
965 FD_SET(fuse_fd, &readfds);
966 nready = select(nfds, &readfds, NULL, NULL,
971 ASSERT_LE(0, nready) << strerror(errno);
972 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
975 FAIL() << "not yet implemented";
977 res = read(m_fuse_fd, &in, sizeof(in));
979 if (res < 0 && !m_quit) {
981 FAIL() << "read: " << strerror(errno);
983 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
985 * Inconsistently, fuse_in_header.len is the size of the entire
986 * request,including header, even though fuse_out_header.len excludes
987 * the size of the header.
989 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
992 void MockFS::write_response(const mockfs_buf_out &out) {
995 struct kevent changes[1];
996 struct kevent events[1];
1004 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1005 EV_ADD | EV_ONESHOT, 0, 0, 0);
1006 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1008 ASSERT_LE(0, nready) << strerror(errno);
1009 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1010 if (events[0].flags & EV_ERROR)
1011 FAIL() << strerror(events[0].data);
1012 else if (events[0].flags & EV_EOF)
1013 FAIL() << strerror(events[0].fflags);
1014 m_nready = events[0].data;
1017 fds[0].fd = m_fuse_fd;
1018 fds[0].events = POLLOUT;
1019 nready = poll(fds, 1, INFTIM);
1020 ASSERT_LE(0, nready) << strerror(errno);
1021 ASSERT_EQ(1, nready) << "NULL timeout expired?";
1022 ASSERT_TRUE(fds[0].revents & POLLOUT);
1026 FD_SET(m_fuse_fd, &writefds);
1027 nfds = m_fuse_fd + 1;
1028 nready = select(nfds, NULL, &writefds, NULL, NULL);
1029 ASSERT_LE(0, nready) << strerror(errno);
1030 ASSERT_EQ(1, nready) << "NULL timeout expired?";
1031 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1034 FAIL() << "not yet implemented";
1036 r = write(m_fuse_fd, &out, out.header.len);
1037 if (m_expected_write_errno) {
1039 ASSERT_EQ(m_expected_write_errno, errno) << strerror(errno);
1041 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1045 void* MockFS::service(void *pthr_data) {
1046 MockFS *mock_fs = (MockFS*)pthr_data;
1053 void MockFS::unmount() {
1054 ::unmount("mountpoint", 0);