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 156c2d70d6eSAlan 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; 171a1c9f4adSAlan Somers case FUSE_BMAP: 172a1c9f4adSAlan Somers printf(" block=%#lx blocksize=%#x", in.body.bmap.block, 173a1c9f4adSAlan Somers in.body.bmap.blocksize); 174a1c9f4adSAlan Somers break; 17519ef317dSAlan Somers case FUSE_CREATE: 176a4856c96SAlan Somers if (m_kernel_minor_version >= 12) 177a4856c96SAlan Somers name = (const char*)in.body.bytes + 178a4856c96SAlan Somers sizeof(fuse_create_in); 179a4856c96SAlan Somers else 18029edc611SAlan Somers name = (const char*)in.body.bytes + 18119ef317dSAlan Somers sizeof(fuse_open_in); 18219ef317dSAlan Somers printf(" flags=%#x name=%s", 18329edc611SAlan Somers in.body.open.flags, name); 18419ef317dSAlan Somers break; 1859821f1d3SAlan Somers case FUSE_FLUSH: 186cc04566cSAlan Somers printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64, 18729edc611SAlan Somers in.body.flush.fh, 18829edc611SAlan Somers in.body.flush.lock_owner); 1899821f1d3SAlan Somers break; 1909821f1d3SAlan Somers case FUSE_FORGET: 19129edc611SAlan Somers printf(" nlookup=%" PRIu64, in.body.forget.nlookup); 1929821f1d3SAlan Somers break; 1939821f1d3SAlan Somers case FUSE_FSYNC: 19429edc611SAlan Somers printf(" flags=%#x", in.body.fsync.fsync_flags); 1959821f1d3SAlan Somers break; 1969821f1d3SAlan Somers case FUSE_FSYNCDIR: 19729edc611SAlan Somers printf(" flags=%#x", in.body.fsyncdir.fsync_flags); 1989821f1d3SAlan Somers break; 199723c7768SAlan Somers case FUSE_INTERRUPT: 20029edc611SAlan Somers printf(" unique=%" PRIu64, in.body.interrupt.unique); 201723c7768SAlan Somers break; 202002e54b0SAlan Somers case FUSE_LINK: 20329edc611SAlan Somers printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 204002e54b0SAlan Somers break; 2059821f1d3SAlan Somers case FUSE_LOOKUP: 20629edc611SAlan Somers printf(" %s", in.body.lookup); 2079821f1d3SAlan Somers break; 20899cf7bffSAlan Somers case FUSE_MKDIR: 20929edc611SAlan Somers name = (const char*)in.body.bytes + 21099cf7bffSAlan Somers sizeof(fuse_mkdir_in); 211a4856c96SAlan Somers printf(" name=%s mode=%#o umask=%#o", name, 212a4856c96SAlan Somers in.body.mkdir.mode, in.body.mkdir.umask); 21399cf7bffSAlan Somers break; 214bf4d7084SAlan Somers case FUSE_MKNOD: 215a4856c96SAlan Somers if (m_kernel_minor_version >= 12) 216a4856c96SAlan Somers name = (const char*)in.body.bytes + 217a4856c96SAlan Somers sizeof(fuse_mknod_in); 218a4856c96SAlan Somers else 219a4856c96SAlan Somers name = (const char*)in.body.bytes + 220a4856c96SAlan Somers FUSE_COMPAT_MKNOD_IN_SIZE; 221a4856c96SAlan Somers printf(" mode=%#o rdev=%x umask=%#o name=%s", 222a4856c96SAlan Somers in.body.mknod.mode, in.body.mknod.rdev, 223a4856c96SAlan Somers in.body.mknod.umask, name); 224bf4d7084SAlan Somers break; 2259821f1d3SAlan Somers case FUSE_OPEN: 226a4856c96SAlan Somers printf(" flags=%#x", in.body.open.flags); 2279821f1d3SAlan Somers break; 2289821f1d3SAlan Somers case FUSE_OPENDIR: 229a4856c96SAlan Somers printf(" flags=%#x", in.body.opendir.flags); 2309821f1d3SAlan Somers break; 2319821f1d3SAlan Somers case FUSE_READ: 232cc04566cSAlan Somers printf(" offset=%" PRIu64 " size=%u", 23329edc611SAlan Somers in.body.read.offset, 23429edc611SAlan Somers in.body.read.size); 235d4fd0c81SAlan Somers if (verbosity > 1) 236d4fd0c81SAlan Somers printf(" flags=%#x", in.body.read.flags); 2379821f1d3SAlan Somers break; 2389821f1d3SAlan Somers case FUSE_READDIR: 239cc04566cSAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 24029edc611SAlan Somers in.body.readdir.fh, in.body.readdir.offset, 24129edc611SAlan Somers in.body.readdir.size); 2429821f1d3SAlan Somers break; 2439821f1d3SAlan Somers case FUSE_RELEASE: 244cc04566cSAlan Somers printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 24529edc611SAlan Somers in.body.release.fh, 24629edc611SAlan Somers in.body.release.flags, 24729edc611SAlan Somers in.body.release.lock_owner); 2489821f1d3SAlan Somers break; 2499821f1d3SAlan Somers case FUSE_SETATTR: 2509821f1d3SAlan Somers if (verbosity <= 1) { 25129edc611SAlan Somers printf(" valid=%#x", in.body.setattr.valid); 2529821f1d3SAlan Somers break; 2539821f1d3SAlan Somers } 25429edc611SAlan Somers if (in.body.setattr.valid & FATTR_MODE) 25529edc611SAlan Somers printf(" mode=%#o", in.body.setattr.mode); 25629edc611SAlan Somers if (in.body.setattr.valid & FATTR_UID) 25729edc611SAlan Somers printf(" uid=%u", in.body.setattr.uid); 25829edc611SAlan Somers if (in.body.setattr.valid & FATTR_GID) 25929edc611SAlan Somers printf(" gid=%u", in.body.setattr.gid); 26029edc611SAlan Somers if (in.body.setattr.valid & FATTR_SIZE) 26129edc611SAlan Somers printf(" size=%" PRIu64, in.body.setattr.size); 26229edc611SAlan Somers if (in.body.setattr.valid & FATTR_ATIME) 263cc04566cSAlan Somers printf(" atime=%" PRIu64 ".%u", 26429edc611SAlan Somers in.body.setattr.atime, 26529edc611SAlan Somers in.body.setattr.atimensec); 26629edc611SAlan Somers if (in.body.setattr.valid & FATTR_MTIME) 267cc04566cSAlan Somers printf(" mtime=%" PRIu64 ".%u", 26829edc611SAlan Somers in.body.setattr.mtime, 26929edc611SAlan Somers in.body.setattr.mtimensec); 27029edc611SAlan Somers if (in.body.setattr.valid & FATTR_FH) 27129edc611SAlan Somers printf(" fh=%" PRIu64 "", in.body.setattr.fh); 2729821f1d3SAlan Somers break; 273f067b609SAlan Somers case FUSE_SETLK: 274cc04566cSAlan Somers printf(" fh=%#" PRIx64 " owner=%" PRIu64 275cc04566cSAlan Somers " type=%u pid=%u", 27629edc611SAlan Somers in.body.setlk.fh, in.body.setlk.owner, 27729edc611SAlan Somers in.body.setlk.lk.type, 27829edc611SAlan Somers in.body.setlk.lk.pid); 279f067b609SAlan Somers if (verbosity >= 2) { 280cc04566cSAlan Somers printf(" range=[%" PRIu64 "-%" PRIu64 "]", 28129edc611SAlan Somers in.body.setlk.lk.start, 28229edc611SAlan Somers in.body.setlk.lk.end); 283f067b609SAlan Somers } 284f067b609SAlan Somers break; 2859821f1d3SAlan Somers case FUSE_SETXATTR: 2869821f1d3SAlan Somers /* 2879821f1d3SAlan Somers * In theory neither the xattr name and value need be 2889821f1d3SAlan Somers * ASCII, but in this test suite they always are. 2899821f1d3SAlan Somers */ 29029edc611SAlan Somers name = (const char*)in.body.bytes + 2919821f1d3SAlan Somers sizeof(fuse_setxattr_in); 29219ef317dSAlan Somers value = name + strlen(name) + 1; 29319ef317dSAlan Somers printf(" %s=%s", name, value); 2949821f1d3SAlan Somers break; 2959821f1d3SAlan Somers case FUSE_WRITE: 296cc04566cSAlan Somers printf(" fh=%#" PRIx64 " offset=%" PRIu64 297d4fd0c81SAlan Somers " size=%u write_flags=%u", 29829edc611SAlan Somers in.body.write.fh, 29929edc611SAlan Somers in.body.write.offset, in.body.write.size, 30029edc611SAlan Somers in.body.write.write_flags); 301d4fd0c81SAlan Somers if (verbosity > 1) 302d4fd0c81SAlan Somers printf(" flags=%#x", in.body.write.flags); 3039821f1d3SAlan Somers break; 3049821f1d3SAlan Somers default: 3059821f1d3SAlan Somers break; 3069821f1d3SAlan Somers } 3079821f1d3SAlan Somers printf("\n"); 3089821f1d3SAlan Somers } 3099821f1d3SAlan Somers 310c2d70d6eSAlan Somers /* 311c2d70d6eSAlan Somers * Debug a FUSE response. 312c2d70d6eSAlan Somers * 313c2d70d6eSAlan Somers * This is mostly useful for asynchronous notifications, which don't correspond 314c2d70d6eSAlan Somers * to any request 315c2d70d6eSAlan Somers */ 316c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) { 317c2d70d6eSAlan Somers const char *name; 318c2d70d6eSAlan Somers 319c2d70d6eSAlan Somers if (verbosity == 0) 320c2d70d6eSAlan Somers return; 321c2d70d6eSAlan Somers 322c2d70d6eSAlan Somers switch (out.header.error) { 323c2d70d6eSAlan Somers case FUSE_NOTIFY_INVAL_ENTRY: 324c2d70d6eSAlan Somers name = (const char*)out.body.bytes + 325c2d70d6eSAlan Somers sizeof(fuse_notify_inval_entry_out); 326c2d70d6eSAlan Somers printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 327c2d70d6eSAlan Somers out.body.inval_entry.parent, name); 328c2d70d6eSAlan Somers break; 329eae1ae13SAlan Somers case FUSE_NOTIFY_INVAL_INODE: 330eae1ae13SAlan Somers printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64 331eae1ae13SAlan Somers " len=%" PRIi64 "\n", 332eae1ae13SAlan Somers out.body.inval_inode.ino, 333eae1ae13SAlan Somers out.body.inval_inode.off, 334eae1ae13SAlan Somers out.body.inval_inode.len); 335eae1ae13SAlan Somers break; 3367cbb8e8aSAlan Somers case FUSE_NOTIFY_STORE: 3377cbb8e8aSAlan Somers printf("<- STORE ino=%" PRIu64 " off=%" PRIu64 3387cbb8e8aSAlan Somers " size=%" PRIu32 "\n", 3397cbb8e8aSAlan Somers out.body.store.nodeid, 3407cbb8e8aSAlan Somers out.body.store.offset, 3417cbb8e8aSAlan Somers out.body.store.size); 3427cbb8e8aSAlan Somers break; 343c2d70d6eSAlan Somers default: 344c2d70d6eSAlan Somers break; 345c2d70d6eSAlan Somers } 346c2d70d6eSAlan Somers } 347c2d70d6eSAlan Somers 34891ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, 34916bd2d47SAlan Somers bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 350402b609cSAlan Somers uint32_t kernel_minor_version, uint32_t max_write, bool async, 351402b609cSAlan Somers bool noclusterr) 3529821f1d3SAlan Somers { 3538b73a4c5SAlan Somers struct sigaction sa; 3549821f1d3SAlan Somers struct iovec *iov = NULL; 3559821f1d3SAlan Somers int iovlen = 0; 3569821f1d3SAlan Somers char fdstr[15]; 35791ff3a0dSAlan Somers const bool trueval = true; 3589821f1d3SAlan Somers 3599821f1d3SAlan Somers m_daemon_id = NULL; 36016bd2d47SAlan Somers m_kernel_minor_version = kernel_minor_version; 3619821f1d3SAlan Somers m_maxreadahead = max_readahead; 3628eecd9ceSAlan Somers m_maxwrite = max_write; 3630a7c63e0SAlan Somers m_nready = -1; 3643429092cSAlan Somers m_pm = pm; 36581a619c4SAlan Somers m_quit = false; 3663429092cSAlan Somers if (m_pm == KQ) 3673429092cSAlan Somers m_kq = kqueue(); 3683429092cSAlan Somers else 3693429092cSAlan Somers m_kq = -1; 3709821f1d3SAlan Somers 3719821f1d3SAlan Somers /* 3729821f1d3SAlan Somers * Kyua sets pwd to a testcase-unique tempdir; no need to use 3739821f1d3SAlan Somers * mkdtemp 3749821f1d3SAlan Somers */ 3759821f1d3SAlan Somers /* 3769821f1d3SAlan Somers * googletest doesn't allow ASSERT_ in constructors, so we must throw 3779821f1d3SAlan Somers * instead. 3789821f1d3SAlan Somers */ 37991ff3a0dSAlan Somers if (mkdir("mountpoint" , 0755) && errno != EEXIST) 3809821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 3819821f1d3SAlan Somers "Couldn't make mountpoint directory")); 3829821f1d3SAlan Somers 3833429092cSAlan Somers switch (m_pm) { 3843429092cSAlan Somers case BLOCKING: 38591ff3a0dSAlan Somers m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 3863429092cSAlan Somers break; 3873429092cSAlan Somers default: 3883429092cSAlan Somers m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 3893429092cSAlan Somers break; 3903429092cSAlan Somers } 3919821f1d3SAlan Somers if (m_fuse_fd < 0) 3929821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 3939821f1d3SAlan Somers "Couldn't open /dev/fuse")); 3949821f1d3SAlan Somers 3959821f1d3SAlan Somers m_pid = getpid(); 39691ff3a0dSAlan Somers m_child_pid = -1; 3979821f1d3SAlan Somers 3989821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 3999821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fspath", 4009821f1d3SAlan Somers __DECONST(void *, "mountpoint"), -1); 4019821f1d3SAlan Somers build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 4023429092cSAlan Somers sprintf(fdstr, "%d", m_fuse_fd); 4039821f1d3SAlan Somers build_iovec(&iov, &iovlen, "fd", fdstr, -1); 40491ff3a0dSAlan Somers if (allow_other) { 40591ff3a0dSAlan Somers build_iovec(&iov, &iovlen, "allow_other", 4069821f1d3SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 4079821f1d3SAlan Somers } 4089821f1d3SAlan Somers if (default_permissions) { 4099821f1d3SAlan Somers build_iovec(&iov, &iovlen, "default_permissions", 4109821f1d3SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 4119821f1d3SAlan Somers } 41291ff3a0dSAlan Somers if (push_symlinks_in) { 41391ff3a0dSAlan Somers build_iovec(&iov, &iovlen, "push_symlinks_in", 41491ff3a0dSAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 41591ff3a0dSAlan Somers } 416140bb492SAlan Somers if (ro) { 417140bb492SAlan Somers build_iovec(&iov, &iovlen, "ro", 418140bb492SAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 419140bb492SAlan Somers } 4208eecd9ceSAlan Somers if (async) { 4218eecd9ceSAlan Somers build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), 4228eecd9ceSAlan Somers sizeof(bool)); 4238eecd9ceSAlan Somers } 424402b609cSAlan Somers if (noclusterr) { 425402b609cSAlan Somers build_iovec(&iov, &iovlen, "noclusterr", 426402b609cSAlan Somers __DECONST(void*, &trueval), sizeof(bool)); 427402b609cSAlan Somers } 4289821f1d3SAlan Somers if (nmount(iov, iovlen, 0)) 4299821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 4309821f1d3SAlan Somers "Couldn't mount filesystem")); 4319821f1d3SAlan Somers 4329821f1d3SAlan Somers // Setup default handler 4339821f1d3SAlan Somers ON_CALL(*this, process(_, _)) 4349821f1d3SAlan Somers .WillByDefault(Invoke(this, &MockFS::process_default)); 4359821f1d3SAlan Somers 4369821f1d3SAlan Somers init(flags); 4378b73a4c5SAlan Somers bzero(&sa, sizeof(sa)); 4388b73a4c5SAlan Somers sa.sa_handler = sigint_handler; 4398b73a4c5SAlan Somers sa.sa_flags = 0; /* Don't set SA_RESTART! */ 4408b73a4c5SAlan Somers if (0 != sigaction(SIGUSR1, &sa, NULL)) 4418b73a4c5SAlan Somers throw(std::system_error(errno, std::system_category(), 4428b73a4c5SAlan Somers "Couldn't handle SIGUSR1")); 4439821f1d3SAlan Somers if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 4449821f1d3SAlan Somers throw(std::system_error(errno, std::system_category(), 4459821f1d3SAlan Somers "Couldn't Couldn't start fuse thread")); 4469821f1d3SAlan Somers } 4479821f1d3SAlan Somers 4489821f1d3SAlan Somers MockFS::~MockFS() { 4499821f1d3SAlan Somers kill_daemon(); 4509821f1d3SAlan Somers if (m_daemon_id != NULL) { 4519821f1d3SAlan Somers pthread_join(m_daemon_id, NULL); 4529821f1d3SAlan Somers m_daemon_id = NULL; 4539821f1d3SAlan Somers } 4548b73a4c5SAlan Somers ::unmount("mountpoint", MNT_FORCE); 4559821f1d3SAlan Somers rmdir("mountpoint"); 4563429092cSAlan Somers if (m_kq >= 0) 4573429092cSAlan Somers close(m_kq); 4589821f1d3SAlan Somers } 4599821f1d3SAlan Somers 4609821f1d3SAlan Somers void MockFS::init(uint32_t flags) { 46129edc611SAlan Somers std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 46229edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 4639821f1d3SAlan Somers 46429edc611SAlan Somers read_request(*in); 4659821f1d3SAlan Somers ASSERT_EQ(FUSE_INIT, in->header.opcode); 4669821f1d3SAlan Somers 4679821f1d3SAlan Somers out->header.unique = in->header.unique; 4689821f1d3SAlan Somers out->header.error = 0; 4699821f1d3SAlan Somers out->body.init.major = FUSE_KERNEL_VERSION; 47016bd2d47SAlan Somers out->body.init.minor = m_kernel_minor_version;; 4719821f1d3SAlan Somers out->body.init.flags = in->body.init.flags & flags; 4728eecd9ceSAlan Somers out->body.init.max_write = m_maxwrite; 4739821f1d3SAlan Somers out->body.init.max_readahead = m_maxreadahead; 474*87ff949aSAlan Somers 475*87ff949aSAlan Somers if (m_kernel_minor_version < 23) { 476*87ff949aSAlan Somers SET_OUT_HEADER_LEN(*out, init_7_22); 477*87ff949aSAlan Somers } else { 47829edc611SAlan Somers SET_OUT_HEADER_LEN(*out, init); 479*87ff949aSAlan Somers } 480*87ff949aSAlan Somers 48129edc611SAlan Somers write(m_fuse_fd, out.get(), out->header.len); 4829821f1d3SAlan Somers } 4839821f1d3SAlan Somers 4849821f1d3SAlan Somers void MockFS::kill_daemon() { 48581a619c4SAlan Somers m_quit = true; 4868b73a4c5SAlan Somers if (m_daemon_id != NULL) 4879821f1d3SAlan Somers pthread_kill(m_daemon_id, SIGUSR1); 4888b73a4c5SAlan Somers // Closing the /dev/fuse file descriptor first allows unmount to 4898b73a4c5SAlan Somers // succeed even if the daemon doesn't correctly respond to commands 4908b73a4c5SAlan Somers // during the unmount sequence. 4919821f1d3SAlan Somers close(m_fuse_fd); 4928b73a4c5SAlan Somers m_fuse_fd = -1; 4939821f1d3SAlan Somers } 4949821f1d3SAlan Somers 4959821f1d3SAlan Somers void MockFS::loop() { 49629edc611SAlan Somers std::vector<std::unique_ptr<mockfs_buf_out>> out; 4979821f1d3SAlan Somers 49829edc611SAlan Somers std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 4999821f1d3SAlan Somers ASSERT_TRUE(in != NULL); 50081a619c4SAlan Somers while (!m_quit) { 50129edc611SAlan Somers bzero(in.get(), sizeof(*in)); 50229edc611SAlan Somers read_request(*in); 50381a619c4SAlan Somers if (m_quit) 5049821f1d3SAlan Somers break; 5059821f1d3SAlan Somers if (verbosity > 0) 506c2d70d6eSAlan Somers debug_request(*in); 5079821f1d3SAlan Somers if (pid_ok((pid_t)in->header.pid)) { 50829edc611SAlan Somers process(*in, out); 5099821f1d3SAlan Somers } else { 5109821f1d3SAlan Somers /* 5119821f1d3SAlan Somers * Reject any requests from unknown processes. Because 5129821f1d3SAlan Somers * we actually do mount a filesystem, plenty of 5139821f1d3SAlan Somers * unrelated system daemons may try to access it. 5149821f1d3SAlan Somers */ 51599cf7bffSAlan Somers if (verbosity > 1) 51699cf7bffSAlan Somers printf("\tREJECTED (wrong pid %d)\n", 51799cf7bffSAlan Somers in->header.pid); 51829edc611SAlan Somers process_default(*in, out); 5199821f1d3SAlan Somers } 52029edc611SAlan Somers for (auto &it: out) 52129edc611SAlan Somers write_response(*it); 5229821f1d3SAlan Somers out.clear(); 5239821f1d3SAlan Somers } 5249821f1d3SAlan Somers } 5259821f1d3SAlan Somers 526c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) 527c2d70d6eSAlan Somers { 528c2d70d6eSAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 529c2d70d6eSAlan Somers 530c2d70d6eSAlan Somers out->header.unique = 0; /* 0 means asynchronous notification */ 531c2d70d6eSAlan Somers out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 532c2d70d6eSAlan Somers out->body.inval_entry.parent = parent; 533c2d70d6eSAlan Somers out->body.inval_entry.namelen = namelen; 534c2d70d6eSAlan Somers strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 535c2d70d6eSAlan Somers name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 536c2d70d6eSAlan Somers out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 537c2d70d6eSAlan Somers namelen; 538c2d70d6eSAlan Somers debug_response(*out); 539c2d70d6eSAlan Somers write_response(*out); 540c2d70d6eSAlan Somers return 0; 541c2d70d6eSAlan Somers } 542c2d70d6eSAlan Somers 543eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len) 544eae1ae13SAlan Somers { 545eae1ae13SAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 546eae1ae13SAlan Somers 547eae1ae13SAlan Somers out->header.unique = 0; /* 0 means asynchronous notification */ 548eae1ae13SAlan Somers out->header.error = FUSE_NOTIFY_INVAL_INODE; 549eae1ae13SAlan Somers out->body.inval_inode.ino = ino; 550eae1ae13SAlan Somers out->body.inval_inode.off = off; 551eae1ae13SAlan Somers out->body.inval_inode.len = len; 552eae1ae13SAlan Somers out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode); 553eae1ae13SAlan Somers debug_response(*out); 554eae1ae13SAlan Somers write_response(*out); 555eae1ae13SAlan Somers return 0; 556eae1ae13SAlan Somers } 557eae1ae13SAlan Somers 5587cbb8e8aSAlan Somers int MockFS::notify_store(ino_t ino, off_t off, void* data, ssize_t size) 5597cbb8e8aSAlan Somers { 5607cbb8e8aSAlan Somers std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 5617cbb8e8aSAlan Somers 5627cbb8e8aSAlan Somers out->header.unique = 0; /* 0 means asynchronous notification */ 5637cbb8e8aSAlan Somers out->header.error = FUSE_NOTIFY_STORE; 5647cbb8e8aSAlan Somers out->body.store.nodeid = ino; 5657cbb8e8aSAlan Somers out->body.store.offset = off; 5667cbb8e8aSAlan Somers out->body.store.size = size; 5677cbb8e8aSAlan Somers bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size); 5687cbb8e8aSAlan Somers out->header.len = sizeof(out->header) + sizeof(out->body.store) + size; 5697cbb8e8aSAlan Somers debug_response(*out); 5707cbb8e8aSAlan Somers write_response(*out); 5717cbb8e8aSAlan Somers return 0; 5727cbb8e8aSAlan Somers } 5737cbb8e8aSAlan Somers 5749821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) { 5759821f1d3SAlan Somers if (pid == m_pid) { 5769821f1d3SAlan Somers return (true); 57791ff3a0dSAlan Somers } else if (pid == m_child_pid) { 57891ff3a0dSAlan Somers return (true); 5799821f1d3SAlan Somers } else { 5809821f1d3SAlan Somers struct kinfo_proc *ki; 5819821f1d3SAlan Somers bool ok = false; 5829821f1d3SAlan Somers 5839821f1d3SAlan Somers ki = kinfo_getproc(pid); 5849821f1d3SAlan Somers if (ki == NULL) 5859821f1d3SAlan Somers return (false); 5869821f1d3SAlan Somers /* 5879821f1d3SAlan Somers * Allow access by the aio daemon processes so that our tests 5889821f1d3SAlan Somers * can use aio functions 5899821f1d3SAlan Somers */ 5909821f1d3SAlan Somers if (0 == strncmp("aiod", ki->ki_comm, 4)) 5919821f1d3SAlan Somers ok = true; 5929821f1d3SAlan Somers free(ki); 5939821f1d3SAlan Somers return (ok); 5949821f1d3SAlan Somers } 5959821f1d3SAlan Somers } 5969821f1d3SAlan Somers 59729edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in, 59829edc611SAlan Somers std::vector<std::unique_ptr<mockfs_buf_out>> &out) 5999821f1d3SAlan Somers { 60029edc611SAlan Somers std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 60129edc611SAlan Somers out0->header.unique = in.header.unique; 6029821f1d3SAlan Somers out0->header.error = -EOPNOTSUPP; 6039821f1d3SAlan Somers out0->header.len = sizeof(out0->header); 60429edc611SAlan Somers out.push_back(std::move(out0)); 6059821f1d3SAlan Somers } 6069821f1d3SAlan Somers 60729edc611SAlan Somers void MockFS::read_request(mockfs_buf_in &in) { 6089821f1d3SAlan Somers ssize_t res; 60977fbe694SAlan Somers int nready = 0; 6103429092cSAlan Somers fd_set readfds; 6113429092cSAlan Somers pollfd fds[1]; 6123429092cSAlan Somers struct kevent changes[1]; 6133429092cSAlan Somers struct kevent events[1]; 61477fbe694SAlan Somers struct timespec timeout_ts; 61577fbe694SAlan Somers struct timeval timeout_tv; 61677fbe694SAlan Somers const int timeout_ms = 999; 61777fbe694SAlan Somers int timeout_int, nfds; 6189821f1d3SAlan Somers 6193429092cSAlan Somers switch (m_pm) { 6203429092cSAlan Somers case BLOCKING: 6213429092cSAlan Somers break; 6223429092cSAlan Somers case KQ: 62377fbe694SAlan Somers timeout_ts.tv_sec = 0; 62477fbe694SAlan Somers timeout_ts.tv_nsec = timeout_ms * 1'000'000; 62577fbe694SAlan Somers while (nready == 0) { 62677fbe694SAlan Somers EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0, 62777fbe694SAlan Somers 0, 0); 62877fbe694SAlan Somers nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 62977fbe694SAlan Somers &timeout_ts); 6303429092cSAlan Somers if (m_quit) 6313429092cSAlan Somers return; 63277fbe694SAlan Somers } 6333429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 6343429092cSAlan Somers ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 6353429092cSAlan Somers if (events[0].flags & EV_ERROR) 6363429092cSAlan Somers FAIL() << strerror(events[0].data); 6373429092cSAlan Somers else if (events[0].flags & EV_EOF) 6383429092cSAlan Somers FAIL() << strerror(events[0].fflags); 6390a7c63e0SAlan Somers m_nready = events[0].data; 6403429092cSAlan Somers break; 6413429092cSAlan Somers case POLL: 64277fbe694SAlan Somers timeout_int = timeout_ms; 6433429092cSAlan Somers fds[0].fd = m_fuse_fd; 6443429092cSAlan Somers fds[0].events = POLLIN; 64577fbe694SAlan Somers while (nready == 0) { 64677fbe694SAlan Somers nready = poll(fds, 1, timeout_int); 6473429092cSAlan Somers if (m_quit) 6483429092cSAlan Somers return; 64977fbe694SAlan Somers } 6503429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 6513429092cSAlan Somers ASSERT_TRUE(fds[0].revents & POLLIN); 6523429092cSAlan Somers break; 6533429092cSAlan Somers case SELECT: 65477fbe694SAlan Somers timeout_tv.tv_sec = 0; 65577fbe694SAlan Somers timeout_tv.tv_usec = timeout_ms * 1'000; 65677fbe694SAlan Somers nfds = m_fuse_fd + 1; 65777fbe694SAlan Somers while (nready == 0) { 6583429092cSAlan Somers FD_ZERO(&readfds); 6593429092cSAlan Somers FD_SET(m_fuse_fd, &readfds); 66077fbe694SAlan Somers nready = select(nfds, &readfds, NULL, NULL, 66177fbe694SAlan Somers &timeout_tv); 6623429092cSAlan Somers if (m_quit) 6633429092cSAlan Somers return; 66477fbe694SAlan Somers } 6653429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 6663429092cSAlan Somers ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds)); 6673429092cSAlan Somers break; 6683429092cSAlan Somers default: 6693429092cSAlan Somers FAIL() << "not yet implemented"; 6703429092cSAlan Somers } 67129edc611SAlan Somers res = read(m_fuse_fd, &in, sizeof(in)); 6723429092cSAlan Somers 673b690d120SAlan Somers if (res < 0 && !m_quit) { 674b690d120SAlan Somers FAIL() << "read: " << strerror(errno); 675b690d120SAlan Somers m_quit = true; 676b690d120SAlan Somers } 67729edc611SAlan Somers ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 6789821f1d3SAlan Somers } 6799821f1d3SAlan Somers 68029edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) { 6813429092cSAlan Somers fd_set writefds; 6823429092cSAlan Somers pollfd fds[1]; 6833429092cSAlan Somers int nready, nfds; 6843429092cSAlan Somers ssize_t r; 6853429092cSAlan Somers 6863429092cSAlan Somers switch (m_pm) { 6873429092cSAlan Somers case BLOCKING: 6883429092cSAlan Somers case KQ: /* EVFILT_WRITE is not supported */ 6893429092cSAlan Somers break; 6903429092cSAlan Somers case POLL: 6913429092cSAlan Somers fds[0].fd = m_fuse_fd; 6923429092cSAlan Somers fds[0].events = POLLOUT; 6933429092cSAlan Somers nready = poll(fds, 1, INFTIM); 6943429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 6953429092cSAlan Somers ASSERT_EQ(1, nready) << "NULL timeout expired?"; 6963429092cSAlan Somers ASSERT_TRUE(fds[0].revents & POLLOUT); 6973429092cSAlan Somers break; 6983429092cSAlan Somers case SELECT: 6993429092cSAlan Somers FD_ZERO(&writefds); 7003429092cSAlan Somers FD_SET(m_fuse_fd, &writefds); 7013429092cSAlan Somers nfds = m_fuse_fd + 1; 7023429092cSAlan Somers nready = select(nfds, NULL, &writefds, NULL, NULL); 7033429092cSAlan Somers ASSERT_LE(0, nready) << strerror(errno); 7043429092cSAlan Somers ASSERT_EQ(1, nready) << "NULL timeout expired?"; 7053429092cSAlan Somers ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 7063429092cSAlan Somers break; 7073429092cSAlan Somers default: 7083429092cSAlan Somers FAIL() << "not yet implemented"; 7093429092cSAlan Somers } 71029edc611SAlan Somers r = write(m_fuse_fd, &out, out.header.len); 7113429092cSAlan Somers ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 7123429092cSAlan Somers } 7133429092cSAlan Somers 7149821f1d3SAlan Somers void* MockFS::service(void *pthr_data) { 7159821f1d3SAlan Somers MockFS *mock_fs = (MockFS*)pthr_data; 7169821f1d3SAlan Somers 7179821f1d3SAlan Somers mock_fs->loop(); 7189821f1d3SAlan Somers 7199821f1d3SAlan Somers return (NULL); 7209821f1d3SAlan Somers } 7219821f1d3SAlan Somers 7229821f1d3SAlan Somers void MockFS::unmount() { 7239821f1d3SAlan Somers ::unmount("mountpoint", 0); 7249821f1d3SAlan Somers } 725