19821f1d3SAlan Somers /*- 29821f1d3SAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39821f1d3SAlan Somers * 49821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 59821f1d3SAlan Somers * 69821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 79821f1d3SAlan Somers * from the FreeBSD Foundation. 89821f1d3SAlan Somers * 99821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without 109821f1d3SAlan Somers * modification, are permitted provided that the following conditions 119821f1d3SAlan Somers * are met: 129821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright 139821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer. 149821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 159821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the 169821f1d3SAlan Somers * documentation and/or other materials provided with the distribution. 179821f1d3SAlan Somers * 189821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 199821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 209821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 219821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 229821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 239821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 249821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 259821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 269821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 279821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 289821f1d3SAlan Somers * SUCH DAMAGE. 299821f1d3SAlan Somers */ 309821f1d3SAlan Somers 319821f1d3SAlan Somers extern "C" { 329821f1d3SAlan Somers #include <sys/param.h> 339821f1d3SAlan Somers 349821f1d3SAlan Somers #include <sys/mount.h> 353429092cSAlan Somers #include <sys/select.h> 369821f1d3SAlan Somers #include <sys/stat.h> 379821f1d3SAlan Somers #include <sys/uio.h> 389821f1d3SAlan Somers #include <sys/user.h> 399821f1d3SAlan Somers 409821f1d3SAlan Somers #include <fcntl.h> 419821f1d3SAlan Somers #include <libutil.h> 423429092cSAlan Somers #include <poll.h> 439821f1d3SAlan Somers #include <pthread.h> 449821f1d3SAlan Somers #include <signal.h> 459821f1d3SAlan Somers #include <stdlib.h> 469821f1d3SAlan Somers #include <unistd.h> 479821f1d3SAlan Somers 489821f1d3SAlan Somers #include "mntopts.h" // for build_iovec 499821f1d3SAlan Somers } 509821f1d3SAlan Somers 51cc04566cSAlan Somers #include <cinttypes> 52cc04566cSAlan Somers 539821f1d3SAlan Somers #include <gtest/gtest.h> 549821f1d3SAlan Somers 559821f1d3SAlan Somers #include "mockfs.hh" 569821f1d3SAlan Somers 579821f1d3SAlan Somers using namespace testing; 589821f1d3SAlan Somers 599821f1d3SAlan Somers int verbosity = 0; 609821f1d3SAlan Somers 619821f1d3SAlan Somers const char* opcode2opname(uint32_t opcode) 629821f1d3SAlan Somers { 639821f1d3SAlan Somers const int NUM_OPS = 39; 649821f1d3SAlan Somers const char* table[NUM_OPS] = { 659821f1d3SAlan Somers "Unknown (opcode 0)", 669821f1d3SAlan Somers "LOOKUP", 679821f1d3SAlan Somers "FORGET", 689821f1d3SAlan Somers "GETATTR", 699821f1d3SAlan Somers "SETATTR", 709821f1d3SAlan Somers "READLINK", 719821f1d3SAlan Somers "SYMLINK", 729821f1d3SAlan Somers "Unknown (opcode 7)", 739821f1d3SAlan Somers "MKNOD", 749821f1d3SAlan Somers "MKDIR", 759821f1d3SAlan Somers "UNLINK", 769821f1d3SAlan Somers "RMDIR", 779821f1d3SAlan Somers "RENAME", 789821f1d3SAlan Somers "LINK", 799821f1d3SAlan Somers "OPEN", 809821f1d3SAlan Somers "READ", 819821f1d3SAlan Somers "WRITE", 829821f1d3SAlan Somers "STATFS", 839821f1d3SAlan Somers "RELEASE", 849821f1d3SAlan Somers "Unknown (opcode 19)", 859821f1d3SAlan Somers "FSYNC", 869821f1d3SAlan Somers "SETXATTR", 879821f1d3SAlan Somers "GETXATTR", 889821f1d3SAlan Somers "LISTXATTR", 899821f1d3SAlan Somers "REMOVEXATTR", 909821f1d3SAlan Somers "FLUSH", 919821f1d3SAlan Somers "INIT", 929821f1d3SAlan Somers "OPENDIR", 939821f1d3SAlan Somers "READDIR", 949821f1d3SAlan Somers "RELEASEDIR", 959821f1d3SAlan Somers "FSYNCDIR", 969821f1d3SAlan Somers "GETLK", 979821f1d3SAlan Somers "SETLK", 989821f1d3SAlan Somers "SETLKW", 999821f1d3SAlan Somers "ACCESS", 1009821f1d3SAlan Somers "CREATE", 1019821f1d3SAlan Somers "INTERRUPT", 1029821f1d3SAlan Somers "BMAP", 1039821f1d3SAlan Somers "DESTROY" 1049821f1d3SAlan Somers }; 1059821f1d3SAlan Somers if (opcode >= NUM_OPS) 1069821f1d3SAlan Somers return ("Unknown (opcode > max)"); 1079821f1d3SAlan Somers else 1089821f1d3SAlan Somers return (table[opcode]); 1099821f1d3SAlan Somers } 1109821f1d3SAlan Somers 1119821f1d3SAlan Somers ProcessMockerT 1129821f1d3SAlan Somers ReturnErrno(int error) 1139821f1d3SAlan Somers { 1149821f1d3SAlan Somers return([=](auto in, auto &out) { 115*29edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 116*29edc611SAlan Somers out0->header.unique = in.header.unique; 1179821f1d3SAlan Somers out0->header.error = -error; 1189821f1d3SAlan Somers out0->header.len = sizeof(out0->header); 119*29edc611SAlan Somers out.push_back(std::move(out0)); 1209821f1d3SAlan Somers }); 1219821f1d3SAlan Somers } 1229821f1d3SAlan Somers 1239821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */ 1249821f1d3SAlan Somers ProcessMockerT 1259821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid) 1269821f1d3SAlan Somers { 1279821f1d3SAlan Somers return([=](auto in, auto &out) { 1289821f1d3SAlan Somers /* nodeid means ENOENT and cache it */ 129*29edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 1309821f1d3SAlan Somers out0->body.entry.nodeid = 0; 131*29edc611SAlan Somers out0->header.unique = in.header.unique; 1329821f1d3SAlan Somers out0->header.error = 0; 1339821f1d3SAlan Somers out0->body.entry.entry_valid = entry_valid->tv_sec; 1349821f1d3SAlan Somers out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec; 135*29edc611SAlan Somers SET_OUT_HEADER_LEN(*out0, entry); 136*29edc611SAlan Somers out.push_back(std::move(out0)); 1379821f1d3SAlan Somers }); 1389821f1d3SAlan Somers } 1399821f1d3SAlan Somers 1409821f1d3SAlan Somers ProcessMockerT 141*29edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in, 142*29edc611SAlan Somers struct mockfs_buf_out &out)> f) 1439821f1d3SAlan Somers { 144*29edc611SAlan Somers return([=](auto& in, auto &out) { 145*29edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 146*29edc611SAlan Somers out0->header.unique = in.header.unique; 147*29edc611SAlan Somers f(in, *out0); 148*29edc611SAlan Somers out.push_back(std::move(out0)); 1499821f1d3SAlan Somers }); 1509821f1d3SAlan Somers } 1519821f1d3SAlan Somers 1529821f1d3SAlan Somers void sigint_handler(int __unused sig) { 1538b73a4c5SAlan Somers // Don't do anything except interrupt the daemon's read(2) call 1549821f1d3SAlan Somers } 1559821f1d3SAlan Somers 156*29edc611SAlan Somers void debug_fuseop(const mockfs_buf_in &in) 1579821f1d3SAlan Somers { 158*29edc611SAlan Somers printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode), 159*29edc611SAlan Somers in.header.nodeid); 1609821f1d3SAlan Somers if (verbosity > 1) { 161cc04566cSAlan Somers printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u", 162*29edc611SAlan Somers in.header.uid, in.header.gid, in.header.pid, 163*29edc611SAlan Somers in.header.unique, in.header.len); 1649821f1d3SAlan Somers } 165*29edc611SAlan Somers switch (in.header.opcode) { 16619ef317dSAlan Somers const char *name, *value; 16719ef317dSAlan Somers 168caf5f57dSAlan Somers case FUSE_ACCESS: 169*29edc611SAlan Somers printf(" mask=%#x", in.body.access.mask); 170caf5f57dSAlan Somers break; 17119ef317dSAlan Somers case FUSE_CREATE: 172*29edc611SAlan Somers name = (const char*)in.body.bytes + 17319ef317dSAlan Somers sizeof(fuse_open_in); 17419ef317dSAlan Somers printf(" flags=%#x name=%s", 175*29edc611SAlan Somers in.body.open.flags, name); 17619ef317dSAlan Somers break; 1779821f1d3SAlan Somers case FUSE_FLUSH: 178cc04566cSAlan Somers printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64, 179*29edc611SAlan Somers in.body.flush.fh, 180*29edc611SAlan Somers in.body.flush.lock_owner); 1819821f1d3SAlan Somers break; 1829821f1d3SAlan Somers case FUSE_FORGET: 183*29edc611SAlan Somers printf(" nlookup=%" PRIu64, in.body.forget.nlookup); 1849821f1d3SAlan Somers break; 1859821f1d3SAlan Somers case FUSE_FSYNC: 186*29edc611SAlan Somers printf(" flags=%#x", in.body.fsync.fsync_flags); 1879821f1d3SAlan Somers break; 1889821f1d3SAlan Somers case FUSE_FSYNCDIR: 189*29edc611SAlan Somers printf(" flags=%#x", in.body.fsyncdir.fsync_flags); 1909821f1d3SAlan Somers break; 191723c7768SAlan Somers case FUSE_INTERRUPT: 192*29edc611SAlan Somers printf(" unique=%" PRIu64, in.body.interrupt.unique); 193723c7768SAlan Somers break; 194002e54b0SAlan Somers case FUSE_LINK: 195*29edc611SAlan Somers printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 196002e54b0SAlan Somers break; 1979821f1d3SAlan Somers case FUSE_LOOKUP: 198*29edc611SAlan Somers printf(" %s", in.body.lookup); 1999821f1d3SAlan Somers break; 20099cf7bffSAlan Somers case FUSE_MKDIR: 201*29edc611SAlan Somers name = (const char*)in.body.bytes + 20299cf7bffSAlan Somers sizeof(fuse_mkdir_in); 203*29edc611SAlan Somers printf(" name=%s mode=%#o", name, in.body.mkdir.mode); 20499cf7bffSAlan Somers break; 205bf4d7084SAlan Somers case FUSE_MKNOD: 206*29edc611SAlan Somers printf(" mode=%#o rdev=%x", in.body.mknod.mode, 207*29edc611SAlan Somers in.body.mknod.rdev); 208bf4d7084SAlan Somers break; 2099821f1d3SAlan Somers case FUSE_OPEN: 2109821f1d3SAlan Somers printf(" flags=%#x mode=%#o", 211*29edc611SAlan Somers in.body.open.flags, in.body.open.mode); 2129821f1d3SAlan Somers break; 2139821f1d3SAlan Somers case FUSE_OPENDIR: 2149821f1d3SAlan Somers printf(" flags=%#x mode=%#o", 215*29edc611SAlan Somers in.body.opendir.flags, in.body.opendir.mode); 2169821f1d3SAlan Somers break; 2179821f1d3SAlan Somers case FUSE_READ: 218cc04566cSAlan Somers printf(" offset=%" PRIu64 " size=%u", 219*29edc611SAlan Somers in.body.read.offset, 220*29edc611SAlan Somers in.body.read.size); 2219821f1d3SAlan Somers break; 2229821f1d3SAlan Somers case FUSE_READDIR: 223cc04566cSAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 224*29edc611SAlan Somers in.body.readdir.fh, in.body.readdir.offset, 225*29edc611SAlan Somers in.body.readdir.size); 2269821f1d3SAlan Somers break; 2279821f1d3SAlan Somers case FUSE_RELEASE: 228cc04566cSAlan Somers printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 229*29edc611SAlan Somers in.body.release.fh, 230*29edc611SAlan Somers in.body.release.flags, 231*29edc611SAlan Somers in.body.release.lock_owner); 2329821f1d3SAlan Somers break; 2339821f1d3SAlan Somers case FUSE_SETATTR: 2349821f1d3SAlan Somers if (verbosity <= 1) { 235*29edc611SAlan Somers printf(" valid=%#x", in.body.setattr.valid); 2369821f1d3SAlan Somers break; 2379821f1d3SAlan Somers } 238*29edc611SAlan Somers if (in.body.setattr.valid & FATTR_MODE) 239*29edc611SAlan Somers printf(" mode=%#o", in.body.setattr.mode); 240*29edc611SAlan Somers if (in.body.setattr.valid & FATTR_UID) 241*29edc611SAlan Somers printf(" uid=%u", in.body.setattr.uid); 242*29edc611SAlan Somers if (in.body.setattr.valid & FATTR_GID) 243*29edc611SAlan Somers printf(" gid=%u", in.body.setattr.gid); 244*29edc611SAlan Somers if (in.body.setattr.valid & FATTR_SIZE) 245*29edc611SAlan Somers printf(" size=%" PRIu64, in.body.setattr.size); 246*29edc611SAlan Somers if (in.body.setattr.valid & FATTR_ATIME) 247cc04566cSAlan Somers printf(" atime=%" PRIu64 ".%u", 248*29edc611SAlan Somers in.body.setattr.atime, 249*29edc611SAlan Somers in.body.setattr.atimensec); 250*29edc611SAlan Somers if (in.body.setattr.valid & FATTR_MTIME) 251cc04566cSAlan Somers printf(" mtime=%" PRIu64 ".%u", 252*29edc611SAlan Somers in.body.setattr.mtime, 253*29edc611SAlan Somers in.body.setattr.mtimensec); 254*29edc611SAlan Somers if (in.body.setattr.valid & FATTR_FH) 255*29edc611SAlan Somers printf(" fh=%" PRIu64 "", in.body.setattr.fh); 2569821f1d3SAlan Somers break; 257f067b609SAlan Somers case FUSE_SETLK: 258cc04566cSAlan Somers printf(" fh=%#" PRIx64 " owner=%" PRIu64 259cc04566cSAlan Somers " type=%u pid=%u", 260*29edc611SAlan Somers in.body.setlk.fh, in.body.setlk.owner, 261*29edc611SAlan Somers in.body.setlk.lk.type, 262*29edc611SAlan Somers in.body.setlk.lk.pid); 263f067b609SAlan Somers if (verbosity >= 2) { 264cc04566cSAlan Somers printf(" range=[%" PRIu64 "-%" PRIu64 "]", 265*29edc611SAlan Somers in.body.setlk.lk.start, 266*29edc611SAlan Somers in.body.setlk.lk.end); 267f067b609SAlan Somers } 268f067b609SAlan Somers break; 2699821f1d3SAlan Somers case FUSE_SETXATTR: 2709821f1d3SAlan Somers /* 2719821f1d3SAlan Somers * In theory neither the xattr name and value need be 2729821f1d3SAlan Somers * ASCII, but in this test suite they always are. 2739821f1d3SAlan Somers */ 274*29edc611SAlan Somers name = (const char*)in.body.bytes + 2759821f1d3SAlan Somers sizeof(fuse_setxattr_in); 27619ef317dSAlan Somers value = name + strlen(name) + 1; 27719ef317dSAlan Somers printf(" %s=%s", name, value); 2789821f1d3SAlan Somers break; 2799821f1d3SAlan Somers case FUSE_WRITE: 280cc04566cSAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64 281cc04566cSAlan Somers " size=%u flags=%u", 282*29edc611SAlan Somers in.body.write.fh, 283*29edc611SAlan Somers in.body.write.offset, in.body.write.size, 284*29edc611SAlan Somers in.body.write.write_flags); 2859821f1d3SAlan Somers break; 2869821f1d3SAlan Somers default: 2879821f1d3SAlan Somers break; 2889821f1d3SAlan Somers } 2899821f1d3SAlan Somers printf("\n"); 2909821f1d3SAlan Somers } 2919821f1d3SAlan Somers 29291ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, 29316bd2d47SAlan Somers bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 29416bd2d47SAlan Somers uint32_t kernel_minor_version) 2959821f1d3SAlan Somers { 2968b73a4c5SAlan Somers struct sigaction sa; 2979821f1d3SAlan Somers struct iovec *iov = NULL; 2989821f1d3SAlan Somers int iovlen = 0; 2999821f1d3SAlan Somers char fdstr[15]; 30091ff3a0dSAlan Somers const bool trueval = true; 3019821f1d3SAlan Somers 3029821f1d3SAlan Somers m_daemon_id = NULL; 30316bd2d47SAlan Somers m_kernel_minor_version = kernel_minor_version; 3049821f1d3SAlan Somers m_maxreadahead = max_readahead; 3050a7c63e0SAlan Somers m_nready = -1; 3063429092cSAlan Somers m_pm = pm; 30781a619c4SAlan Somers m_quit = false; 3083429092cSAlan Somers if (m_pm == KQ) 3093429092cSAlan Somers m_kq = kqueue(); 3103429092cSAlan Somers else 3113429092cSAlan Somers m_kq = -1; 3129821f1d3SAlan Somers 3139821f1d3SAlan Somers /* 3149821f1d3SAlan Somers * Kyua sets pwd to a testcase-unique tempdir; no need to use 3159821f1d3SAlan Somers * mkdtemp 3169821f1d3SAlan Somers */ 3179821f1d3SAlan Somers /* 3189821f1d3SAlan Somers * googletest doesn't allow ASSERT_ in constructors, so we must throw 3199821f1d3SAlan Somers * instead. 3209821f1d3SAlan Somers */ 32191ff3a0dSAlan Somers if (mkdir("mountpoint" , 0755) && errno != EEXIST) 3229821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 3239821f1d3SAlan Somers "Couldn't make mountpoint directory")); 3249821f1d3SAlan Somers 3253429092cSAlan Somers switch (m_pm) { 3263429092cSAlan Somers case BLOCKING: 32791ff3a0dSAlan Somers m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 3283429092cSAlan Somers break; 3293429092cSAlan Somers default: 3303429092cSAlan Somers m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 3313429092cSAlan Somers break; 3323429092cSAlan Somers } 3339821f1d3SAlan Somers if (m_fuse_fd < 0) 3349821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 3359821f1d3SAlan Somers "Couldn't open /dev/fuse")); 3369821f1d3SAlan Somers 3379821f1d3SAlan Somers m_pid = getpid(); 33891ff3a0dSAlan Somers m_child_pid = -1; 3399821f1d3SAlan Somers 3409821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 3419821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fspath", 3429821f1d3SAlan Somers __DECONST(void *, "mountpoint"), -1); 3439821f1d3SAlan Somers build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 3443429092cSAlan Somers sprintf(fdstr, "%d", m_fuse_fd); 3459821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fd", fdstr, -1); 34691ff3a0dSAlan Somers if (allow_other) { 34791ff3a0dSAlan Somers build_iovec(&iov, &iovlen, "allow_other", 3489821f1d3SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 3499821f1d3SAlan Somers } 3509821f1d3SAlan Somers if (default_permissions) { 3519821f1d3SAlan Somers build_iovec(&iov, &iovlen, "default_permissions", 3529821f1d3SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 3539821f1d3SAlan Somers } 35491ff3a0dSAlan Somers if (push_symlinks_in) { 35591ff3a0dSAlan Somers build_iovec(&iov, &iovlen, "push_symlinks_in", 35691ff3a0dSAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 35791ff3a0dSAlan Somers } 358140bb492SAlan Somers if (ro) { 359140bb492SAlan Somers build_iovec(&iov, &iovlen, "ro", 360140bb492SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 361140bb492SAlan Somers } 3629821f1d3SAlan Somers if (nmount(iov, iovlen, 0)) 3639821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 3649821f1d3SAlan Somers "Couldn't mount filesystem")); 3659821f1d3SAlan Somers 3669821f1d3SAlan Somers // Setup default handler 3679821f1d3SAlan Somers ON_CALL(*this, process(_, _)) 3689821f1d3SAlan Somers .WillByDefault(Invoke(this, &MockFS::process_default)); 3699821f1d3SAlan Somers 3709821f1d3SAlan Somers init(flags); 3718b73a4c5SAlan Somers bzero(&sa, sizeof(sa)); 3728b73a4c5SAlan Somers sa.sa_handler = sigint_handler; 3738b73a4c5SAlan Somers sa.sa_flags = 0; /* Don't set SA_RESTART! */ 3748b73a4c5SAlan Somers if (0 != sigaction(SIGUSR1, &sa, NULL)) 3758b73a4c5SAlan Somers throw(std::system_error(errno, std::system_category(), 3768b73a4c5SAlan Somers "Couldn't handle SIGUSR1")); 3779821f1d3SAlan Somers if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 3789821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 3799821f1d3SAlan Somers "Couldn't Couldn't start fuse thread")); 3809821f1d3SAlan Somers } 3819821f1d3SAlan Somers 3829821f1d3SAlan Somers MockFS::~MockFS() { 3839821f1d3SAlan Somers kill_daemon(); 3849821f1d3SAlan Somers if (m_daemon_id != NULL) { 3859821f1d3SAlan Somers pthread_join(m_daemon_id, NULL); 3869821f1d3SAlan Somers m_daemon_id = NULL; 3879821f1d3SAlan Somers } 3888b73a4c5SAlan Somers ::unmount("mountpoint", MNT_FORCE); 3899821f1d3SAlan Somers rmdir("mountpoint"); 3903429092cSAlan Somers if (m_kq >= 0) 3913429092cSAlan Somers close(m_kq); 3929821f1d3SAlan Somers } 3939821f1d3SAlan Somers 3949821f1d3SAlan Somers void MockFS::init(uint32_t flags) { 395*29edc611SAlan Somers std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 396*29edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 3979821f1d3SAlan Somers 398*29edc611SAlan Somers read_request(*in); 3999821f1d3SAlan Somers ASSERT_EQ(FUSE_INIT, in->header.opcode); 4009821f1d3SAlan Somers 4019821f1d3SAlan Somers out->header.unique = in->header.unique; 4029821f1d3SAlan Somers out->header.error = 0; 4039821f1d3SAlan Somers out->body.init.major = FUSE_KERNEL_VERSION; 40416bd2d47SAlan Somers out->body.init.minor = m_kernel_minor_version;; 4059821f1d3SAlan Somers out->body.init.flags = in->body.init.flags & flags; 4069821f1d3SAlan Somers 4079821f1d3SAlan Somers /* 4089821f1d3SAlan Somers * The default max_write is set to this formula in libfuse, though 4099821f1d3SAlan Somers * individual filesystems can lower it. The "- 4096" was added in 4109821f1d3SAlan Somers * commit 154ffe2, with the commit message "fix". 4119821f1d3SAlan Somers */ 4129821f1d3SAlan Somers uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096; 4139821f1d3SAlan Somers /* For testing purposes, it should be distinct from MAXPHYS */ 4149821f1d3SAlan Somers m_max_write = MIN(default_max_write, MAXPHYS / 2); 4159821f1d3SAlan Somers out->body.init.max_write = m_max_write; 4169821f1d3SAlan Somers 4179821f1d3SAlan Somers out->body.init.max_readahead = m_maxreadahead; 418*29edc611SAlan Somers SET_OUT_HEADER_LEN(*out, init); 419*29edc611SAlan Somers write(m_fuse_fd, out.get(), out->header.len); 4209821f1d3SAlan Somers } 4219821f1d3SAlan Somers 4229821f1d3SAlan Somers void MockFS::kill_daemon() { 42381a619c4SAlan Somers m_quit = true; 4248b73a4c5SAlan Somers if (m_daemon_id != NULL) 4259821f1d3SAlan Somers pthread_kill(m_daemon_id, SIGUSR1); 4268b73a4c5SAlan Somers // Closing the /dev/fuse file descriptor first allows unmount to 4278b73a4c5SAlan Somers // succeed even if the daemon doesn't correctly respond to commands 4288b73a4c5SAlan Somers // during the unmount sequence. 4299821f1d3SAlan Somers close(m_fuse_fd); 4308b73a4c5SAlan Somers m_fuse_fd = -1; 4319821f1d3SAlan Somers } 4329821f1d3SAlan Somers 4339821f1d3SAlan Somers void MockFS::loop() { 434*29edc611SAlan Somers std::vector<std::unique_ptr<mockfs_buf_out>> out; 4359821f1d3SAlan Somers 436*29edc611SAlan Somers std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 4379821f1d3SAlan Somers ASSERT_TRUE(in != NULL); 43881a619c4SAlan Somers while (!m_quit) { 439*29edc611SAlan Somers bzero(in.get(), sizeof(*in)); 440*29edc611SAlan Somers read_request(*in); 44181a619c4SAlan Somers if (m_quit) 4429821f1d3SAlan Somers break; 4439821f1d3SAlan Somers if (verbosity > 0) 444*29edc611SAlan Somers debug_fuseop(*in); 4459821f1d3SAlan Somers if (pid_ok((pid_t)in->header.pid)) { 446*29edc611SAlan Somers process(*in, out); 4479821f1d3SAlan Somers } else { 4489821f1d3SAlan Somers /* 4499821f1d3SAlan Somers * Reject any requests from unknown processes. Because 4509821f1d3SAlan Somers * we actually do mount a filesystem, plenty of 4519821f1d3SAlan Somers * unrelated system daemons may try to access it. 4529821f1d3SAlan Somers */ 45399cf7bffSAlan Somers if (verbosity > 1) 45499cf7bffSAlan Somers printf("\tREJECTED (wrong pid %d)\n", 45599cf7bffSAlan Somers in->header.pid); 456*29edc611SAlan Somers process_default(*in, out); 4579821f1d3SAlan Somers } 458*29edc611SAlan Somers for (auto &it: out) 459*29edc611SAlan Somers write_response(*it); 4609821f1d3SAlan Somers out.clear(); 4619821f1d3SAlan Somers } 4629821f1d3SAlan Somers } 4639821f1d3SAlan Somers 4649821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) { 4659821f1d3SAlan Somers if (pid == m_pid) { 4669821f1d3SAlan Somers return (true); 46791ff3a0dSAlan Somers } else if (pid == m_child_pid) { 46891ff3a0dSAlan Somers return (true); 4699821f1d3SAlan Somers } else { 4709821f1d3SAlan Somers struct kinfo_proc *ki; 4719821f1d3SAlan Somers bool ok = false; 4729821f1d3SAlan Somers 4739821f1d3SAlan Somers ki = kinfo_getproc(pid); 4749821f1d3SAlan Somers if (ki == NULL) 4759821f1d3SAlan Somers return (false); 4769821f1d3SAlan Somers /* 4779821f1d3SAlan Somers * Allow access by the aio daemon processes so that our tests 4789821f1d3SAlan Somers * can use aio functions 4799821f1d3SAlan Somers */ 4809821f1d3SAlan Somers if (0 == strncmp("aiod", ki->ki_comm, 4)) 4819821f1d3SAlan Somers ok = true; 4829821f1d3SAlan Somers free(ki); 4839821f1d3SAlan Somers return (ok); 4849821f1d3SAlan Somers } 4859821f1d3SAlan Somers } 4869821f1d3SAlan Somers 487*29edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in, 488*29edc611SAlan Somers std::vector<std::unique_ptr<mockfs_buf_out>> &out) 4899821f1d3SAlan Somers { 490*29edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 491*29edc611SAlan Somers out0->header.unique = in.header.unique; 4929821f1d3SAlan Somers out0->header.error = -EOPNOTSUPP; 4939821f1d3SAlan Somers out0->header.len = sizeof(out0->header); 494*29edc611SAlan Somers out.push_back(std::move(out0)); 4959821f1d3SAlan Somers } 4969821f1d3SAlan Somers 497*29edc611SAlan Somers void MockFS::read_request(mockfs_buf_in &in) { 4989821f1d3SAlan Somers ssize_t res; 49977fbe694SAlan Somers int nready = 0; 5003429092cSAlan Somers fd_set readfds; 5013429092cSAlan Somers pollfd fds[1]; 5023429092cSAlan Somers struct kevent changes[1]; 5033429092cSAlan Somers struct kevent events[1]; 50477fbe694SAlan Somers struct timespec timeout_ts; 50577fbe694SAlan Somers struct timeval timeout_tv; 50677fbe694SAlan Somers const int timeout_ms = 999; 50777fbe694SAlan Somers int timeout_int, nfds; 5089821f1d3SAlan Somers 5093429092cSAlan Somers switch (m_pm) { 5103429092cSAlan Somers case BLOCKING: 5113429092cSAlan Somers break; 5123429092cSAlan Somers case KQ: 51377fbe694SAlan Somers timeout_ts.tv_sec = 0; 51477fbe694SAlan Somers timeout_ts.tv_nsec = timeout_ms * 1'000'000; 51577fbe694SAlan Somers while (nready == 0) { 51677fbe694SAlan Somers EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0, 51777fbe694SAlan Somers 0, 0); 51877fbe694SAlan Somers nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 51977fbe694SAlan Somers &timeout_ts); 5203429092cSAlan Somers if (m_quit) 5213429092cSAlan Somers return; 52277fbe694SAlan Somers } 5233429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 5243429092cSAlan Somers ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 5253429092cSAlan Somers if (events[0].flags & EV_ERROR) 5263429092cSAlan Somers FAIL() << strerror(events[0].data); 5273429092cSAlan Somers else if (events[0].flags & EV_EOF) 5283429092cSAlan Somers FAIL() << strerror(events[0].fflags); 5290a7c63e0SAlan Somers m_nready = events[0].data; 5303429092cSAlan Somers break; 5313429092cSAlan Somers case POLL: 53277fbe694SAlan Somers timeout_int = timeout_ms; 5333429092cSAlan Somers fds[0].fd = m_fuse_fd; 5343429092cSAlan Somers fds[0].events = POLLIN; 53577fbe694SAlan Somers while (nready == 0) { 53677fbe694SAlan Somers nready = poll(fds, 1, timeout_int); 5373429092cSAlan Somers if (m_quit) 5383429092cSAlan Somers return; 53977fbe694SAlan Somers } 5403429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 5413429092cSAlan Somers ASSERT_TRUE(fds[0].revents & POLLIN); 5423429092cSAlan Somers break; 5433429092cSAlan Somers case SELECT: 54477fbe694SAlan Somers timeout_tv.tv_sec = 0; 54577fbe694SAlan Somers timeout_tv.tv_usec = timeout_ms * 1'000; 54677fbe694SAlan Somers nfds = m_fuse_fd + 1; 54777fbe694SAlan Somers while (nready == 0) { 5483429092cSAlan Somers FD_ZERO(&readfds); 5493429092cSAlan Somers FD_SET(m_fuse_fd, &readfds); 55077fbe694SAlan Somers nready = select(nfds, &readfds, NULL, NULL, 55177fbe694SAlan Somers &timeout_tv); 5523429092cSAlan Somers if (m_quit) 5533429092cSAlan Somers return; 55477fbe694SAlan Somers } 5553429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 5563429092cSAlan Somers ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds)); 5573429092cSAlan Somers break; 5583429092cSAlan Somers default: 5593429092cSAlan Somers FAIL() << "not yet implemented"; 5603429092cSAlan Somers } 561*29edc611SAlan Somers res = read(m_fuse_fd, &in, sizeof(in)); 5623429092cSAlan Somers 56381a619c4SAlan Somers if (res < 0 && !m_quit) 5649821f1d3SAlan Somers perror("read"); 565*29edc611SAlan Somers ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 5669821f1d3SAlan Somers } 5679821f1d3SAlan Somers 568*29edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) { 5693429092cSAlan Somers fd_set writefds; 5703429092cSAlan Somers pollfd fds[1]; 5713429092cSAlan Somers int nready, nfds; 5723429092cSAlan Somers ssize_t r; 5733429092cSAlan Somers 5743429092cSAlan Somers switch (m_pm) { 5753429092cSAlan Somers case BLOCKING: 5763429092cSAlan Somers case KQ: /* EVFILT_WRITE is not supported */ 5773429092cSAlan Somers break; 5783429092cSAlan Somers case POLL: 5793429092cSAlan Somers fds[0].fd = m_fuse_fd; 5803429092cSAlan Somers fds[0].events = POLLOUT; 5813429092cSAlan Somers nready = poll(fds, 1, INFTIM); 5823429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 5833429092cSAlan Somers ASSERT_EQ(1, nready) << "NULL timeout expired?"; 5843429092cSAlan Somers ASSERT_TRUE(fds[0].revents & POLLOUT); 5853429092cSAlan Somers break; 5863429092cSAlan Somers case SELECT: 5873429092cSAlan Somers FD_ZERO(&writefds); 5883429092cSAlan Somers FD_SET(m_fuse_fd, &writefds); 5893429092cSAlan Somers nfds = m_fuse_fd + 1; 5903429092cSAlan Somers nready = select(nfds, NULL, &writefds, NULL, NULL); 5913429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 5923429092cSAlan Somers ASSERT_EQ(1, nready) << "NULL timeout expired?"; 5933429092cSAlan Somers ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 5943429092cSAlan Somers break; 5953429092cSAlan Somers default: 5963429092cSAlan Somers FAIL() << "not yet implemented"; 5973429092cSAlan Somers } 598*29edc611SAlan Somers r = write(m_fuse_fd, &out, out.header.len); 5993429092cSAlan Somers ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 6003429092cSAlan Somers } 6013429092cSAlan Somers 6029821f1d3SAlan Somers void* MockFS::service(void *pthr_data) { 6039821f1d3SAlan Somers MockFS *mock_fs = (MockFS*)pthr_data; 6049821f1d3SAlan Somers 6059821f1d3SAlan Somers mock_fs->loop(); 6069821f1d3SAlan Somers 6079821f1d3SAlan Somers return (NULL); 6089821f1d3SAlan Somers } 6099821f1d3SAlan Somers 6109821f1d3SAlan Somers void MockFS::unmount() { 6119821f1d3SAlan Somers ::unmount("mountpoint", 0); 6129821f1d3SAlan Somers } 613