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) { 11529edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 11629edc611SAlan Somers out0->header.unique = in.header.unique; 1179821f1d3SAlan Somers out0->header.error = -error; 1189821f1d3SAlan Somers out0->header.len = sizeof(out0->header); 11929edc611SAlan 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 */ 12929edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 1309821f1d3SAlan Somers out0->body.entry.nodeid = 0; 13129edc611SAlan 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; 13529edc611SAlan Somers SET_OUT_HEADER_LEN(*out0, entry); 13629edc611SAlan Somers out.push_back(std::move(out0)); 1379821f1d3SAlan Somers }); 1389821f1d3SAlan Somers } 1399821f1d3SAlan Somers 1409821f1d3SAlan Somers ProcessMockerT 14129edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in, 14229edc611SAlan Somers struct mockfs_buf_out &out)> f) 1439821f1d3SAlan Somers { 14429edc611SAlan Somers return([=](auto& in, auto &out) { 14529edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 14629edc611SAlan Somers out0->header.unique = in.header.unique; 14729edc611SAlan Somers f(in, *out0); 14829edc611SAlan 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*c2d70d6eSAlan Somers void MockFS::debug_request(const mockfs_buf_in &in) 1579821f1d3SAlan Somers { 15829edc611SAlan Somers printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode), 15929edc611SAlan Somers in.header.nodeid); 1609821f1d3SAlan Somers if (verbosity > 1) { 161cc04566cSAlan Somers printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u", 16229edc611SAlan Somers in.header.uid, in.header.gid, in.header.pid, 16329edc611SAlan Somers in.header.unique, in.header.len); 1649821f1d3SAlan Somers } 16529edc611SAlan Somers switch (in.header.opcode) { 16619ef317dSAlan Somers const char *name, *value; 16719ef317dSAlan Somers 168caf5f57dSAlan Somers case FUSE_ACCESS: 16929edc611SAlan Somers printf(" mask=%#x", in.body.access.mask); 170caf5f57dSAlan Somers break; 17119ef317dSAlan Somers case FUSE_CREATE: 172a4856c96SAlan Somers if (m_kernel_minor_version >= 12) 173a4856c96SAlan Somers name = (const char*)in.body.bytes + 174a4856c96SAlan Somers sizeof(fuse_create_in); 175a4856c96SAlan Somers else 17629edc611SAlan Somers name = (const char*)in.body.bytes + 17719ef317dSAlan Somers sizeof(fuse_open_in); 17819ef317dSAlan Somers printf(" flags=%#x name=%s", 17929edc611SAlan Somers in.body.open.flags, name); 18019ef317dSAlan Somers break; 1819821f1d3SAlan Somers case FUSE_FLUSH: 182cc04566cSAlan Somers printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64, 18329edc611SAlan Somers in.body.flush.fh, 18429edc611SAlan Somers in.body.flush.lock_owner); 1859821f1d3SAlan Somers break; 1869821f1d3SAlan Somers case FUSE_FORGET: 18729edc611SAlan Somers printf(" nlookup=%" PRIu64, in.body.forget.nlookup); 1889821f1d3SAlan Somers break; 1899821f1d3SAlan Somers case FUSE_FSYNC: 19029edc611SAlan Somers printf(" flags=%#x", in.body.fsync.fsync_flags); 1919821f1d3SAlan Somers break; 1929821f1d3SAlan Somers case FUSE_FSYNCDIR: 19329edc611SAlan Somers printf(" flags=%#x", in.body.fsyncdir.fsync_flags); 1949821f1d3SAlan Somers break; 195723c7768SAlan Somers case FUSE_INTERRUPT: 19629edc611SAlan Somers printf(" unique=%" PRIu64, in.body.interrupt.unique); 197723c7768SAlan Somers break; 198002e54b0SAlan Somers case FUSE_LINK: 19929edc611SAlan Somers printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 200002e54b0SAlan Somers break; 2019821f1d3SAlan Somers case FUSE_LOOKUP: 20229edc611SAlan Somers printf(" %s", in.body.lookup); 2039821f1d3SAlan Somers break; 20499cf7bffSAlan Somers case FUSE_MKDIR: 20529edc611SAlan Somers name = (const char*)in.body.bytes + 20699cf7bffSAlan Somers sizeof(fuse_mkdir_in); 207a4856c96SAlan Somers printf(" name=%s mode=%#o umask=%#o", name, 208a4856c96SAlan Somers in.body.mkdir.mode, in.body.mkdir.umask); 20999cf7bffSAlan Somers break; 210bf4d7084SAlan Somers case FUSE_MKNOD: 211a4856c96SAlan Somers if (m_kernel_minor_version >= 12) 212a4856c96SAlan Somers name = (const char*)in.body.bytes + 213a4856c96SAlan Somers sizeof(fuse_mknod_in); 214a4856c96SAlan Somers else 215a4856c96SAlan Somers name = (const char*)in.body.bytes + 216a4856c96SAlan Somers FUSE_COMPAT_MKNOD_IN_SIZE; 217a4856c96SAlan Somers printf(" mode=%#o rdev=%x umask=%#o name=%s", 218a4856c96SAlan Somers in.body.mknod.mode, in.body.mknod.rdev, 219a4856c96SAlan Somers in.body.mknod.umask, name); 220bf4d7084SAlan Somers break; 2219821f1d3SAlan Somers case FUSE_OPEN: 222a4856c96SAlan Somers printf(" flags=%#x", in.body.open.flags); 2239821f1d3SAlan Somers break; 2249821f1d3SAlan Somers case FUSE_OPENDIR: 225a4856c96SAlan Somers printf(" flags=%#x", in.body.opendir.flags); 2269821f1d3SAlan Somers break; 2279821f1d3SAlan Somers case FUSE_READ: 228cc04566cSAlan Somers printf(" offset=%" PRIu64 " size=%u", 22929edc611SAlan Somers in.body.read.offset, 23029edc611SAlan Somers in.body.read.size); 231d4fd0c81SAlan Somers if (verbosity > 1) 232d4fd0c81SAlan Somers printf(" flags=%#x", in.body.read.flags); 2339821f1d3SAlan Somers break; 2349821f1d3SAlan Somers case FUSE_READDIR: 235cc04566cSAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 23629edc611SAlan Somers in.body.readdir.fh, in.body.readdir.offset, 23729edc611SAlan Somers in.body.readdir.size); 2389821f1d3SAlan Somers break; 2399821f1d3SAlan Somers case FUSE_RELEASE: 240cc04566cSAlan Somers printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 24129edc611SAlan Somers in.body.release.fh, 24229edc611SAlan Somers in.body.release.flags, 24329edc611SAlan Somers in.body.release.lock_owner); 2449821f1d3SAlan Somers break; 2459821f1d3SAlan Somers case FUSE_SETATTR: 2469821f1d3SAlan Somers if (verbosity <= 1) { 24729edc611SAlan Somers printf(" valid=%#x", in.body.setattr.valid); 2489821f1d3SAlan Somers break; 2499821f1d3SAlan Somers } 25029edc611SAlan Somers if (in.body.setattr.valid & FATTR_MODE) 25129edc611SAlan Somers printf(" mode=%#o", in.body.setattr.mode); 25229edc611SAlan Somers if (in.body.setattr.valid & FATTR_UID) 25329edc611SAlan Somers printf(" uid=%u", in.body.setattr.uid); 25429edc611SAlan Somers if (in.body.setattr.valid & FATTR_GID) 25529edc611SAlan Somers printf(" gid=%u", in.body.setattr.gid); 25629edc611SAlan Somers if (in.body.setattr.valid & FATTR_SIZE) 25729edc611SAlan Somers printf(" size=%" PRIu64, in.body.setattr.size); 25829edc611SAlan Somers if (in.body.setattr.valid & FATTR_ATIME) 259cc04566cSAlan Somers printf(" atime=%" PRIu64 ".%u", 26029edc611SAlan Somers in.body.setattr.atime, 26129edc611SAlan Somers in.body.setattr.atimensec); 26229edc611SAlan Somers if (in.body.setattr.valid & FATTR_MTIME) 263cc04566cSAlan Somers printf(" mtime=%" PRIu64 ".%u", 26429edc611SAlan Somers in.body.setattr.mtime, 26529edc611SAlan Somers in.body.setattr.mtimensec); 26629edc611SAlan Somers if (in.body.setattr.valid & FATTR_FH) 26729edc611SAlan Somers printf(" fh=%" PRIu64 "", in.body.setattr.fh); 2689821f1d3SAlan Somers break; 269f067b609SAlan Somers case FUSE_SETLK: 270cc04566cSAlan Somers printf(" fh=%#" PRIx64 " owner=%" PRIu64 271cc04566cSAlan Somers " type=%u pid=%u", 27229edc611SAlan Somers in.body.setlk.fh, in.body.setlk.owner, 27329edc611SAlan Somers in.body.setlk.lk.type, 27429edc611SAlan Somers in.body.setlk.lk.pid); 275f067b609SAlan Somers if (verbosity >= 2) { 276cc04566cSAlan Somers printf(" range=[%" PRIu64 "-%" PRIu64 "]", 27729edc611SAlan Somers in.body.setlk.lk.start, 27829edc611SAlan Somers in.body.setlk.lk.end); 279f067b609SAlan Somers } 280f067b609SAlan Somers break; 2819821f1d3SAlan Somers case FUSE_SETXATTR: 2829821f1d3SAlan Somers /* 2839821f1d3SAlan Somers * In theory neither the xattr name and value need be 2849821f1d3SAlan Somers * ASCII, but in this test suite they always are. 2859821f1d3SAlan Somers */ 28629edc611SAlan Somers name = (const char*)in.body.bytes + 2879821f1d3SAlan Somers sizeof(fuse_setxattr_in); 28819ef317dSAlan Somers value = name + strlen(name) + 1; 28919ef317dSAlan Somers printf(" %s=%s", name, value); 2909821f1d3SAlan Somers break; 2919821f1d3SAlan Somers case FUSE_WRITE: 292cc04566cSAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64 293d4fd0c81SAlan Somers " size=%u write_flags=%u", 29429edc611SAlan Somers in.body.write.fh, 29529edc611SAlan Somers in.body.write.offset, in.body.write.size, 29629edc611SAlan Somers in.body.write.write_flags); 297d4fd0c81SAlan Somers if (verbosity > 1) 298d4fd0c81SAlan Somers printf(" flags=%#x", in.body.write.flags); 2999821f1d3SAlan Somers break; 3009821f1d3SAlan Somers default: 3019821f1d3SAlan Somers break; 3029821f1d3SAlan Somers } 3039821f1d3SAlan Somers printf("\n"); 3049821f1d3SAlan Somers } 3059821f1d3SAlan Somers 306*c2d70d6eSAlan Somers /* 307*c2d70d6eSAlan Somers * Debug a FUSE response. 308*c2d70d6eSAlan Somers * 309*c2d70d6eSAlan Somers * This is mostly useful for asynchronous notifications, which don't correspond 310*c2d70d6eSAlan Somers * to any request 311*c2d70d6eSAlan Somers */ 312*c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) { 313*c2d70d6eSAlan Somers const char *name; 314*c2d70d6eSAlan Somers 315*c2d70d6eSAlan Somers if (verbosity == 0) 316*c2d70d6eSAlan Somers return; 317*c2d70d6eSAlan Somers 318*c2d70d6eSAlan Somers switch (out.header.error) { 319*c2d70d6eSAlan Somers case FUSE_NOTIFY_INVAL_ENTRY: 320*c2d70d6eSAlan Somers name = (const char*)out.body.bytes + 321*c2d70d6eSAlan Somers sizeof(fuse_notify_inval_entry_out); 322*c2d70d6eSAlan Somers printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 323*c2d70d6eSAlan Somers out.body.inval_entry.parent, name); 324*c2d70d6eSAlan Somers break; 325*c2d70d6eSAlan Somers default: 326*c2d70d6eSAlan Somers break; 327*c2d70d6eSAlan Somers } 328*c2d70d6eSAlan Somers } 329*c2d70d6eSAlan Somers 33091ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, 33116bd2d47SAlan Somers bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 33216bd2d47SAlan Somers uint32_t kernel_minor_version) 3339821f1d3SAlan Somers { 3348b73a4c5SAlan Somers struct sigaction sa; 3359821f1d3SAlan Somers struct iovec *iov = NULL; 3369821f1d3SAlan Somers int iovlen = 0; 3379821f1d3SAlan Somers char fdstr[15]; 33891ff3a0dSAlan Somers const bool trueval = true; 3399821f1d3SAlan Somers 3409821f1d3SAlan Somers m_daemon_id = NULL; 34116bd2d47SAlan Somers m_kernel_minor_version = kernel_minor_version; 3429821f1d3SAlan Somers m_maxreadahead = max_readahead; 3430a7c63e0SAlan Somers m_nready = -1; 3443429092cSAlan Somers m_pm = pm; 34581a619c4SAlan Somers m_quit = false; 3463429092cSAlan Somers if (m_pm == KQ) 3473429092cSAlan Somers m_kq = kqueue(); 3483429092cSAlan Somers else 3493429092cSAlan Somers m_kq = -1; 3509821f1d3SAlan Somers 3519821f1d3SAlan Somers /* 3529821f1d3SAlan Somers * Kyua sets pwd to a testcase-unique tempdir; no need to use 3539821f1d3SAlan Somers * mkdtemp 3549821f1d3SAlan Somers */ 3559821f1d3SAlan Somers /* 3569821f1d3SAlan Somers * googletest doesn't allow ASSERT_ in constructors, so we must throw 3579821f1d3SAlan Somers * instead. 3589821f1d3SAlan Somers */ 35991ff3a0dSAlan Somers if (mkdir("mountpoint" , 0755) && errno != EEXIST) 3609821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 3619821f1d3SAlan Somers "Couldn't make mountpoint directory")); 3629821f1d3SAlan Somers 3633429092cSAlan Somers switch (m_pm) { 3643429092cSAlan Somers case BLOCKING: 36591ff3a0dSAlan Somers m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 3663429092cSAlan Somers break; 3673429092cSAlan Somers default: 3683429092cSAlan Somers m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 3693429092cSAlan Somers break; 3703429092cSAlan Somers } 3719821f1d3SAlan Somers if (m_fuse_fd < 0) 3729821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 3739821f1d3SAlan Somers "Couldn't open /dev/fuse")); 3749821f1d3SAlan Somers 3759821f1d3SAlan Somers m_pid = getpid(); 37691ff3a0dSAlan Somers m_child_pid = -1; 3779821f1d3SAlan Somers 3789821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 3799821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fspath", 3809821f1d3SAlan Somers __DECONST(void *, "mountpoint"), -1); 3819821f1d3SAlan Somers build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 3823429092cSAlan Somers sprintf(fdstr, "%d", m_fuse_fd); 3839821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fd", fdstr, -1); 38491ff3a0dSAlan Somers if (allow_other) { 38591ff3a0dSAlan Somers build_iovec(&iov, &iovlen, "allow_other", 3869821f1d3SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 3879821f1d3SAlan Somers } 3889821f1d3SAlan Somers if (default_permissions) { 3899821f1d3SAlan Somers build_iovec(&iov, &iovlen, "default_permissions", 3909821f1d3SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 3919821f1d3SAlan Somers } 39291ff3a0dSAlan Somers if (push_symlinks_in) { 39391ff3a0dSAlan Somers build_iovec(&iov, &iovlen, "push_symlinks_in", 39491ff3a0dSAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 39591ff3a0dSAlan Somers } 396140bb492SAlan Somers if (ro) { 397140bb492SAlan Somers build_iovec(&iov, &iovlen, "ro", 398140bb492SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 399140bb492SAlan Somers } 4009821f1d3SAlan Somers if (nmount(iov, iovlen, 0)) 4019821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 4029821f1d3SAlan Somers "Couldn't mount filesystem")); 4039821f1d3SAlan Somers 4049821f1d3SAlan Somers // Setup default handler 4059821f1d3SAlan Somers ON_CALL(*this, process(_, _)) 4069821f1d3SAlan Somers .WillByDefault(Invoke(this, &MockFS::process_default)); 4079821f1d3SAlan Somers 4089821f1d3SAlan Somers init(flags); 4098b73a4c5SAlan Somers bzero(&sa, sizeof(sa)); 4108b73a4c5SAlan Somers sa.sa_handler = sigint_handler; 4118b73a4c5SAlan Somers sa.sa_flags = 0; /* Don't set SA_RESTART! */ 4128b73a4c5SAlan Somers if (0 != sigaction(SIGUSR1, &sa, NULL)) 4138b73a4c5SAlan Somers throw(std::system_error(errno, std::system_category(), 4148b73a4c5SAlan Somers "Couldn't handle SIGUSR1")); 4159821f1d3SAlan Somers if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 4169821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 4179821f1d3SAlan Somers "Couldn't Couldn't start fuse thread")); 4189821f1d3SAlan Somers } 4199821f1d3SAlan Somers 4209821f1d3SAlan Somers MockFS::~MockFS() { 4219821f1d3SAlan Somers kill_daemon(); 4229821f1d3SAlan Somers if (m_daemon_id != NULL) { 4239821f1d3SAlan Somers pthread_join(m_daemon_id, NULL); 4249821f1d3SAlan Somers m_daemon_id = NULL; 4259821f1d3SAlan Somers } 4268b73a4c5SAlan Somers ::unmount("mountpoint", MNT_FORCE); 4279821f1d3SAlan Somers rmdir("mountpoint"); 4283429092cSAlan Somers if (m_kq >= 0) 4293429092cSAlan Somers close(m_kq); 4309821f1d3SAlan Somers } 4319821f1d3SAlan Somers 4329821f1d3SAlan Somers void MockFS::init(uint32_t flags) { 43329edc611SAlan Somers std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 43429edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 4359821f1d3SAlan Somers 43629edc611SAlan Somers read_request(*in); 4379821f1d3SAlan Somers ASSERT_EQ(FUSE_INIT, in->header.opcode); 4389821f1d3SAlan Somers 4399821f1d3SAlan Somers out->header.unique = in->header.unique; 4409821f1d3SAlan Somers out->header.error = 0; 4419821f1d3SAlan Somers out->body.init.major = FUSE_KERNEL_VERSION; 44216bd2d47SAlan Somers out->body.init.minor = m_kernel_minor_version;; 4439821f1d3SAlan Somers out->body.init.flags = in->body.init.flags & flags; 4449821f1d3SAlan Somers 4459821f1d3SAlan Somers /* 4469821f1d3SAlan Somers * The default max_write is set to this formula in libfuse, though 4479821f1d3SAlan Somers * individual filesystems can lower it. The "- 4096" was added in 4489821f1d3SAlan Somers * commit 154ffe2, with the commit message "fix". 4499821f1d3SAlan Somers */ 4509821f1d3SAlan Somers uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096; 4519821f1d3SAlan Somers /* For testing purposes, it should be distinct from MAXPHYS */ 4529821f1d3SAlan Somers m_max_write = MIN(default_max_write, MAXPHYS / 2); 4539821f1d3SAlan Somers out->body.init.max_write = m_max_write; 4549821f1d3SAlan Somers 4559821f1d3SAlan Somers out->body.init.max_readahead = m_maxreadahead; 45629edc611SAlan Somers SET_OUT_HEADER_LEN(*out, init); 45729edc611SAlan Somers write(m_fuse_fd, out.get(), out->header.len); 4589821f1d3SAlan Somers } 4599821f1d3SAlan Somers 4609821f1d3SAlan Somers void MockFS::kill_daemon() { 46181a619c4SAlan Somers m_quit = true; 4628b73a4c5SAlan Somers if (m_daemon_id != NULL) 4639821f1d3SAlan Somers pthread_kill(m_daemon_id, SIGUSR1); 4648b73a4c5SAlan Somers // Closing the /dev/fuse file descriptor first allows unmount to 4658b73a4c5SAlan Somers // succeed even if the daemon doesn't correctly respond to commands 4668b73a4c5SAlan Somers // during the unmount sequence. 4679821f1d3SAlan Somers close(m_fuse_fd); 4688b73a4c5SAlan Somers m_fuse_fd = -1; 4699821f1d3SAlan Somers } 4709821f1d3SAlan Somers 4719821f1d3SAlan Somers void MockFS::loop() { 47229edc611SAlan Somers std::vector<std::unique_ptr<mockfs_buf_out>> out; 4739821f1d3SAlan Somers 47429edc611SAlan Somers std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 4759821f1d3SAlan Somers ASSERT_TRUE(in != NULL); 47681a619c4SAlan Somers while (!m_quit) { 47729edc611SAlan Somers bzero(in.get(), sizeof(*in)); 47829edc611SAlan Somers read_request(*in); 47981a619c4SAlan Somers if (m_quit) 4809821f1d3SAlan Somers break; 4819821f1d3SAlan Somers if (verbosity > 0) 482*c2d70d6eSAlan Somers debug_request(*in); 4839821f1d3SAlan Somers if (pid_ok((pid_t)in->header.pid)) { 48429edc611SAlan Somers process(*in, out); 4859821f1d3SAlan Somers } else { 4869821f1d3SAlan Somers /* 4879821f1d3SAlan Somers * Reject any requests from unknown processes. Because 4889821f1d3SAlan Somers * we actually do mount a filesystem, plenty of 4899821f1d3SAlan Somers * unrelated system daemons may try to access it. 4909821f1d3SAlan Somers */ 49199cf7bffSAlan Somers if (verbosity > 1) 49299cf7bffSAlan Somers printf("\tREJECTED (wrong pid %d)\n", 49399cf7bffSAlan Somers in->header.pid); 49429edc611SAlan Somers process_default(*in, out); 4959821f1d3SAlan Somers } 49629edc611SAlan Somers for (auto &it: out) 49729edc611SAlan Somers write_response(*it); 4989821f1d3SAlan Somers out.clear(); 4999821f1d3SAlan Somers } 5009821f1d3SAlan Somers } 5019821f1d3SAlan Somers 502*c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) 503*c2d70d6eSAlan Somers { 504*c2d70d6eSAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 505*c2d70d6eSAlan Somers 506*c2d70d6eSAlan Somers out->header.unique = 0; /* 0 means asynchronous notification */ 507*c2d70d6eSAlan Somers out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 508*c2d70d6eSAlan Somers out->body.inval_entry.parent = parent; 509*c2d70d6eSAlan Somers out->body.inval_entry.namelen = namelen; 510*c2d70d6eSAlan Somers strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 511*c2d70d6eSAlan Somers name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 512*c2d70d6eSAlan Somers out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 513*c2d70d6eSAlan Somers namelen; 514*c2d70d6eSAlan Somers debug_response(*out); 515*c2d70d6eSAlan Somers write_response(*out); 516*c2d70d6eSAlan Somers return 0; 517*c2d70d6eSAlan Somers } 518*c2d70d6eSAlan Somers 5199821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) { 5209821f1d3SAlan Somers if (pid == m_pid) { 5219821f1d3SAlan Somers return (true); 52291ff3a0dSAlan Somers } else if (pid == m_child_pid) { 52391ff3a0dSAlan Somers return (true); 5249821f1d3SAlan Somers } else { 5259821f1d3SAlan Somers struct kinfo_proc *ki; 5269821f1d3SAlan Somers bool ok = false; 5279821f1d3SAlan Somers 5289821f1d3SAlan Somers ki = kinfo_getproc(pid); 5299821f1d3SAlan Somers if (ki == NULL) 5309821f1d3SAlan Somers return (false); 5319821f1d3SAlan Somers /* 5329821f1d3SAlan Somers * Allow access by the aio daemon processes so that our tests 5339821f1d3SAlan Somers * can use aio functions 5349821f1d3SAlan Somers */ 5359821f1d3SAlan Somers if (0 == strncmp("aiod", ki->ki_comm, 4)) 5369821f1d3SAlan Somers ok = true; 5379821f1d3SAlan Somers free(ki); 5389821f1d3SAlan Somers return (ok); 5399821f1d3SAlan Somers } 5409821f1d3SAlan Somers } 5419821f1d3SAlan Somers 54229edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in, 54329edc611SAlan Somers std::vector<std::unique_ptr<mockfs_buf_out>> &out) 5449821f1d3SAlan Somers { 54529edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 54629edc611SAlan Somers out0->header.unique = in.header.unique; 5479821f1d3SAlan Somers out0->header.error = -EOPNOTSUPP; 5489821f1d3SAlan Somers out0->header.len = sizeof(out0->header); 54929edc611SAlan Somers out.push_back(std::move(out0)); 5509821f1d3SAlan Somers } 5519821f1d3SAlan Somers 55229edc611SAlan Somers void MockFS::read_request(mockfs_buf_in &in) { 5539821f1d3SAlan Somers ssize_t res; 55477fbe694SAlan Somers int nready = 0; 5553429092cSAlan Somers fd_set readfds; 5563429092cSAlan Somers pollfd fds[1]; 5573429092cSAlan Somers struct kevent changes[1]; 5583429092cSAlan Somers struct kevent events[1]; 55977fbe694SAlan Somers struct timespec timeout_ts; 56077fbe694SAlan Somers struct timeval timeout_tv; 56177fbe694SAlan Somers const int timeout_ms = 999; 56277fbe694SAlan Somers int timeout_int, nfds; 5639821f1d3SAlan Somers 5643429092cSAlan Somers switch (m_pm) { 5653429092cSAlan Somers case BLOCKING: 5663429092cSAlan Somers break; 5673429092cSAlan Somers case KQ: 56877fbe694SAlan Somers timeout_ts.tv_sec = 0; 56977fbe694SAlan Somers timeout_ts.tv_nsec = timeout_ms * 1'000'000; 57077fbe694SAlan Somers while (nready == 0) { 57177fbe694SAlan Somers EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0, 57277fbe694SAlan Somers 0, 0); 57377fbe694SAlan Somers nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 57477fbe694SAlan Somers &timeout_ts); 5753429092cSAlan Somers if (m_quit) 5763429092cSAlan Somers return; 57777fbe694SAlan Somers } 5783429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 5793429092cSAlan Somers ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 5803429092cSAlan Somers if (events[0].flags & EV_ERROR) 5813429092cSAlan Somers FAIL() << strerror(events[0].data); 5823429092cSAlan Somers else if (events[0].flags & EV_EOF) 5833429092cSAlan Somers FAIL() << strerror(events[0].fflags); 5840a7c63e0SAlan Somers m_nready = events[0].data; 5853429092cSAlan Somers break; 5863429092cSAlan Somers case POLL: 58777fbe694SAlan Somers timeout_int = timeout_ms; 5883429092cSAlan Somers fds[0].fd = m_fuse_fd; 5893429092cSAlan Somers fds[0].events = POLLIN; 59077fbe694SAlan Somers while (nready == 0) { 59177fbe694SAlan Somers nready = poll(fds, 1, timeout_int); 5923429092cSAlan Somers if (m_quit) 5933429092cSAlan Somers return; 59477fbe694SAlan Somers } 5953429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 5963429092cSAlan Somers ASSERT_TRUE(fds[0].revents & POLLIN); 5973429092cSAlan Somers break; 5983429092cSAlan Somers case SELECT: 59977fbe694SAlan Somers timeout_tv.tv_sec = 0; 60077fbe694SAlan Somers timeout_tv.tv_usec = timeout_ms * 1'000; 60177fbe694SAlan Somers nfds = m_fuse_fd + 1; 60277fbe694SAlan Somers while (nready == 0) { 6033429092cSAlan Somers FD_ZERO(&readfds); 6043429092cSAlan Somers FD_SET(m_fuse_fd, &readfds); 60577fbe694SAlan Somers nready = select(nfds, &readfds, NULL, NULL, 60677fbe694SAlan Somers &timeout_tv); 6073429092cSAlan Somers if (m_quit) 6083429092cSAlan Somers return; 60977fbe694SAlan Somers } 6103429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 6113429092cSAlan Somers ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds)); 6123429092cSAlan Somers break; 6133429092cSAlan Somers default: 6143429092cSAlan Somers FAIL() << "not yet implemented"; 6153429092cSAlan Somers } 61629edc611SAlan Somers res = read(m_fuse_fd, &in, sizeof(in)); 6173429092cSAlan Somers 61881a619c4SAlan Somers if (res < 0 && !m_quit) 6199821f1d3SAlan Somers perror("read"); 62029edc611SAlan Somers ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 6219821f1d3SAlan Somers } 6229821f1d3SAlan Somers 62329edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) { 6243429092cSAlan Somers fd_set writefds; 6253429092cSAlan Somers pollfd fds[1]; 6263429092cSAlan Somers int nready, nfds; 6273429092cSAlan Somers ssize_t r; 6283429092cSAlan Somers 6293429092cSAlan Somers switch (m_pm) { 6303429092cSAlan Somers case BLOCKING: 6313429092cSAlan Somers case KQ: /* EVFILT_WRITE is not supported */ 6323429092cSAlan Somers break; 6333429092cSAlan Somers case POLL: 6343429092cSAlan Somers fds[0].fd = m_fuse_fd; 6353429092cSAlan Somers fds[0].events = POLLOUT; 6363429092cSAlan Somers nready = poll(fds, 1, INFTIM); 6373429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 6383429092cSAlan Somers ASSERT_EQ(1, nready) << "NULL timeout expired?"; 6393429092cSAlan Somers ASSERT_TRUE(fds[0].revents & POLLOUT); 6403429092cSAlan Somers break; 6413429092cSAlan Somers case SELECT: 6423429092cSAlan Somers FD_ZERO(&writefds); 6433429092cSAlan Somers FD_SET(m_fuse_fd, &writefds); 6443429092cSAlan Somers nfds = m_fuse_fd + 1; 6453429092cSAlan Somers nready = select(nfds, NULL, &writefds, NULL, NULL); 6463429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 6473429092cSAlan Somers ASSERT_EQ(1, nready) << "NULL timeout expired?"; 6483429092cSAlan Somers ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 6493429092cSAlan Somers break; 6503429092cSAlan Somers default: 6513429092cSAlan Somers FAIL() << "not yet implemented"; 6523429092cSAlan Somers } 65329edc611SAlan Somers r = write(m_fuse_fd, &out, out.header.len); 6543429092cSAlan Somers ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 6553429092cSAlan Somers } 6563429092cSAlan Somers 6579821f1d3SAlan Somers void* MockFS::service(void *pthr_data) { 6589821f1d3SAlan Somers MockFS *mock_fs = (MockFS*)pthr_data; 6599821f1d3SAlan Somers 6609821f1d3SAlan Somers mock_fs->loop(); 6619821f1d3SAlan Somers 6629821f1d3SAlan Somers return (NULL); 6639821f1d3SAlan Somers } 6649821f1d3SAlan Somers 6659821f1d3SAlan Somers void MockFS::unmount() { 6669821f1d3SAlan Somers ::unmount("mountpoint", 0); 6679821f1d3SAlan Somers } 668