xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision d4fd0c8148b0aa291569a688a400cf6dfdf055c1)
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 
15629edc611SAlan Somers void debug_fuseop(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:
17229edc611SAlan Somers 			name = (const char*)in.body.bytes +
17319ef317dSAlan Somers 				sizeof(fuse_open_in);
17419ef317dSAlan Somers 			printf(" flags=%#x name=%s",
17529edc611SAlan Somers 				in.body.open.flags, name);
17619ef317dSAlan Somers 			break;
1779821f1d3SAlan Somers 		case FUSE_FLUSH:
178cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
17929edc611SAlan Somers 				in.body.flush.fh,
18029edc611SAlan Somers 				in.body.flush.lock_owner);
1819821f1d3SAlan Somers 			break;
1829821f1d3SAlan Somers 		case FUSE_FORGET:
18329edc611SAlan Somers 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
1849821f1d3SAlan Somers 			break;
1859821f1d3SAlan Somers 		case FUSE_FSYNC:
18629edc611SAlan Somers 			printf(" flags=%#x", in.body.fsync.fsync_flags);
1879821f1d3SAlan Somers 			break;
1889821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
18929edc611SAlan Somers 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
1909821f1d3SAlan Somers 			break;
191723c7768SAlan Somers 		case FUSE_INTERRUPT:
19229edc611SAlan Somers 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
193723c7768SAlan Somers 			break;
194002e54b0SAlan Somers 		case FUSE_LINK:
19529edc611SAlan Somers 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
196002e54b0SAlan Somers 			break;
1979821f1d3SAlan Somers 		case FUSE_LOOKUP:
19829edc611SAlan Somers 			printf(" %s", in.body.lookup);
1999821f1d3SAlan Somers 			break;
20099cf7bffSAlan Somers 		case FUSE_MKDIR:
20129edc611SAlan Somers 			name = (const char*)in.body.bytes +
20299cf7bffSAlan Somers 				sizeof(fuse_mkdir_in);
20329edc611SAlan Somers 			printf(" name=%s mode=%#o", name, in.body.mkdir.mode);
20499cf7bffSAlan Somers 			break;
205bf4d7084SAlan Somers 		case FUSE_MKNOD:
20629edc611SAlan Somers 			printf(" mode=%#o rdev=%x", in.body.mknod.mode,
20729edc611SAlan Somers 				in.body.mknod.rdev);
208bf4d7084SAlan Somers 			break;
2099821f1d3SAlan Somers 		case FUSE_OPEN:
2109821f1d3SAlan Somers 			printf(" flags=%#x mode=%#o",
21129edc611SAlan Somers 				in.body.open.flags, in.body.open.mode);
2129821f1d3SAlan Somers 			break;
2139821f1d3SAlan Somers 		case FUSE_OPENDIR:
2149821f1d3SAlan Somers 			printf(" flags=%#x mode=%#o",
21529edc611SAlan Somers 				in.body.opendir.flags, in.body.opendir.mode);
2169821f1d3SAlan Somers 			break;
2179821f1d3SAlan Somers 		case FUSE_READ:
218cc04566cSAlan Somers 			printf(" offset=%" PRIu64 " size=%u",
21929edc611SAlan Somers 				in.body.read.offset,
22029edc611SAlan Somers 				in.body.read.size);
221*d4fd0c81SAlan Somers 			if (verbosity > 1)
222*d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.read.flags);
2239821f1d3SAlan Somers 			break;
2249821f1d3SAlan Somers 		case FUSE_READDIR:
225cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
22629edc611SAlan Somers 				in.body.readdir.fh, in.body.readdir.offset,
22729edc611SAlan Somers 				in.body.readdir.size);
2289821f1d3SAlan Somers 			break;
2299821f1d3SAlan Somers 		case FUSE_RELEASE:
230cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
23129edc611SAlan Somers 				in.body.release.fh,
23229edc611SAlan Somers 				in.body.release.flags,
23329edc611SAlan Somers 				in.body.release.lock_owner);
2349821f1d3SAlan Somers 			break;
2359821f1d3SAlan Somers 		case FUSE_SETATTR:
2369821f1d3SAlan Somers 			if (verbosity <= 1) {
23729edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
2389821f1d3SAlan Somers 				break;
2399821f1d3SAlan Somers 			}
24029edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
24129edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
24229edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
24329edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
24429edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
24529edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
24629edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
24729edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
24829edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
249cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
25029edc611SAlan Somers 					in.body.setattr.atime,
25129edc611SAlan Somers 					in.body.setattr.atimensec);
25229edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
253cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
25429edc611SAlan Somers 					in.body.setattr.mtime,
25529edc611SAlan Somers 					in.body.setattr.mtimensec);
25629edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
25729edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
2589821f1d3SAlan Somers 			break;
259f067b609SAlan Somers 		case FUSE_SETLK:
260cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
261cc04566cSAlan Somers 				" type=%u pid=%u",
26229edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
26329edc611SAlan Somers 				in.body.setlk.lk.type,
26429edc611SAlan Somers 				in.body.setlk.lk.pid);
265f067b609SAlan Somers 			if (verbosity >= 2) {
266cc04566cSAlan Somers 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
26729edc611SAlan Somers 					in.body.setlk.lk.start,
26829edc611SAlan Somers 					in.body.setlk.lk.end);
269f067b609SAlan Somers 			}
270f067b609SAlan Somers 			break;
2719821f1d3SAlan Somers 		case FUSE_SETXATTR:
2729821f1d3SAlan Somers 			/*
2739821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
2749821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
2759821f1d3SAlan Somers 			 */
27629edc611SAlan Somers 			name = (const char*)in.body.bytes +
2779821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
27819ef317dSAlan Somers 			value = name + strlen(name) + 1;
27919ef317dSAlan Somers 			printf(" %s=%s", name, value);
2809821f1d3SAlan Somers 			break;
2819821f1d3SAlan Somers 		case FUSE_WRITE:
282cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
283*d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
28429edc611SAlan Somers 				in.body.write.fh,
28529edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
28629edc611SAlan Somers 				in.body.write.write_flags);
287*d4fd0c81SAlan Somers 			if (verbosity > 1)
288*d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
2899821f1d3SAlan Somers 			break;
2909821f1d3SAlan Somers 		default:
2919821f1d3SAlan Somers 			break;
2929821f1d3SAlan Somers 	}
2939821f1d3SAlan Somers 	printf("\n");
2949821f1d3SAlan Somers }
2959821f1d3SAlan Somers 
29691ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
29716bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
29816bd2d47SAlan Somers 	uint32_t kernel_minor_version)
2999821f1d3SAlan Somers {
3008b73a4c5SAlan Somers 	struct sigaction sa;
3019821f1d3SAlan Somers 	struct iovec *iov = NULL;
3029821f1d3SAlan Somers 	int iovlen = 0;
3039821f1d3SAlan Somers 	char fdstr[15];
30491ff3a0dSAlan Somers 	const bool trueval = true;
3059821f1d3SAlan Somers 
3069821f1d3SAlan Somers 	m_daemon_id = NULL;
30716bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
3089821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
3090a7c63e0SAlan Somers 	m_nready = -1;
3103429092cSAlan Somers 	m_pm = pm;
31181a619c4SAlan Somers 	m_quit = false;
3123429092cSAlan Somers 	if (m_pm == KQ)
3133429092cSAlan Somers 		m_kq = kqueue();
3143429092cSAlan Somers 	else
3153429092cSAlan Somers 		m_kq = -1;
3169821f1d3SAlan Somers 
3179821f1d3SAlan Somers 	/*
3189821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
3199821f1d3SAlan Somers 	 * mkdtemp
3209821f1d3SAlan Somers 	 */
3219821f1d3SAlan Somers 	/*
3229821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
3239821f1d3SAlan Somers 	 * instead.
3249821f1d3SAlan Somers 	 */
32591ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
3269821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3279821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
3289821f1d3SAlan Somers 
3293429092cSAlan Somers 	switch (m_pm) {
3303429092cSAlan Somers 	case BLOCKING:
33191ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
3323429092cSAlan Somers 		break;
3333429092cSAlan Somers 	default:
3343429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
3353429092cSAlan Somers 		break;
3363429092cSAlan Somers 	}
3379821f1d3SAlan Somers 	if (m_fuse_fd < 0)
3389821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3399821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
3409821f1d3SAlan Somers 
3419821f1d3SAlan Somers 	m_pid = getpid();
34291ff3a0dSAlan Somers 	m_child_pid = -1;
3439821f1d3SAlan Somers 
3449821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
3459821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
3469821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
3479821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
3483429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
3499821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
35091ff3a0dSAlan Somers 	if (allow_other) {
35191ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
3529821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
3539821f1d3SAlan Somers 	}
3549821f1d3SAlan Somers 	if (default_permissions) {
3559821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
3569821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
3579821f1d3SAlan Somers 	}
35891ff3a0dSAlan Somers 	if (push_symlinks_in) {
35991ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
36091ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
36191ff3a0dSAlan Somers 	}
362140bb492SAlan Somers 	if (ro) {
363140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
364140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
365140bb492SAlan Somers 	}
3669821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
3679821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3689821f1d3SAlan Somers 			"Couldn't mount filesystem"));
3699821f1d3SAlan Somers 
3709821f1d3SAlan Somers 	// Setup default handler
3719821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
3729821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
3739821f1d3SAlan Somers 
3749821f1d3SAlan Somers 	init(flags);
3758b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
3768b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
3778b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
3788b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
3798b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3808b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
3819821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
3829821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3839821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
3849821f1d3SAlan Somers }
3859821f1d3SAlan Somers 
3869821f1d3SAlan Somers MockFS::~MockFS() {
3879821f1d3SAlan Somers 	kill_daemon();
3889821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
3899821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
3909821f1d3SAlan Somers 		m_daemon_id = NULL;
3919821f1d3SAlan Somers 	}
3928b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
3939821f1d3SAlan Somers 	rmdir("mountpoint");
3943429092cSAlan Somers 	if (m_kq >= 0)
3953429092cSAlan Somers 		close(m_kq);
3969821f1d3SAlan Somers }
3979821f1d3SAlan Somers 
3989821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
39929edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
40029edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
4019821f1d3SAlan Somers 
40229edc611SAlan Somers 	read_request(*in);
4039821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
4049821f1d3SAlan Somers 
4059821f1d3SAlan Somers 	out->header.unique = in->header.unique;
4069821f1d3SAlan Somers 	out->header.error = 0;
4079821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
40816bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
4099821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
4109821f1d3SAlan Somers 
4119821f1d3SAlan Somers 	/*
4129821f1d3SAlan Somers 	 * The default max_write is set to this formula in libfuse, though
4139821f1d3SAlan Somers 	 * individual filesystems can lower it.  The "- 4096" was added in
4149821f1d3SAlan Somers 	 * commit 154ffe2, with the commit message "fix".
4159821f1d3SAlan Somers 	 */
4169821f1d3SAlan Somers 	uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096;
4179821f1d3SAlan Somers 	/* For testing purposes, it should be distinct from MAXPHYS */
4189821f1d3SAlan Somers 	m_max_write = MIN(default_max_write, MAXPHYS / 2);
4199821f1d3SAlan Somers 	out->body.init.max_write = m_max_write;
4209821f1d3SAlan Somers 
4219821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
42229edc611SAlan Somers 	SET_OUT_HEADER_LEN(*out, init);
42329edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
4249821f1d3SAlan Somers }
4259821f1d3SAlan Somers 
4269821f1d3SAlan Somers void MockFS::kill_daemon() {
42781a619c4SAlan Somers 	m_quit = true;
4288b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
4299821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
4308b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
4318b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
4328b73a4c5SAlan Somers 	// during the unmount sequence.
4339821f1d3SAlan Somers 	close(m_fuse_fd);
4348b73a4c5SAlan Somers 	m_fuse_fd = -1;
4359821f1d3SAlan Somers }
4369821f1d3SAlan Somers 
4379821f1d3SAlan Somers void MockFS::loop() {
43829edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
4399821f1d3SAlan Somers 
44029edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
4419821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
44281a619c4SAlan Somers 	while (!m_quit) {
44329edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
44429edc611SAlan Somers 		read_request(*in);
44581a619c4SAlan Somers 		if (m_quit)
4469821f1d3SAlan Somers 			break;
4479821f1d3SAlan Somers 		if (verbosity > 0)
44829edc611SAlan Somers 			debug_fuseop(*in);
4499821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
45029edc611SAlan Somers 			process(*in, out);
4519821f1d3SAlan Somers 		} else {
4529821f1d3SAlan Somers 			/*
4539821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
4549821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
4559821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
4569821f1d3SAlan Somers 			 */
45799cf7bffSAlan Somers 			if (verbosity > 1)
45899cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
45999cf7bffSAlan Somers 					in->header.pid);
46029edc611SAlan Somers 			process_default(*in, out);
4619821f1d3SAlan Somers 		}
46229edc611SAlan Somers 		for (auto &it: out)
46329edc611SAlan Somers 			write_response(*it);
4649821f1d3SAlan Somers 		out.clear();
4659821f1d3SAlan Somers 	}
4669821f1d3SAlan Somers }
4679821f1d3SAlan Somers 
4689821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
4699821f1d3SAlan Somers 	if (pid == m_pid) {
4709821f1d3SAlan Somers 		return (true);
47191ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
47291ff3a0dSAlan Somers 		return (true);
4739821f1d3SAlan Somers 	} else {
4749821f1d3SAlan Somers 		struct kinfo_proc *ki;
4759821f1d3SAlan Somers 		bool ok = false;
4769821f1d3SAlan Somers 
4779821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
4789821f1d3SAlan Somers 		if (ki == NULL)
4799821f1d3SAlan Somers 			return (false);
4809821f1d3SAlan Somers 		/*
4819821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
4829821f1d3SAlan Somers 		 * can use aio functions
4839821f1d3SAlan Somers 		 */
4849821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
4859821f1d3SAlan Somers 			ok = true;
4869821f1d3SAlan Somers 		free(ki);
4879821f1d3SAlan Somers 		return (ok);
4889821f1d3SAlan Somers 	}
4899821f1d3SAlan Somers }
4909821f1d3SAlan Somers 
49129edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
49229edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
4939821f1d3SAlan Somers {
49429edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
49529edc611SAlan Somers 	out0->header.unique = in.header.unique;
4969821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
4979821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
49829edc611SAlan Somers 	out.push_back(std::move(out0));
4999821f1d3SAlan Somers }
5009821f1d3SAlan Somers 
50129edc611SAlan Somers void MockFS::read_request(mockfs_buf_in &in) {
5029821f1d3SAlan Somers 	ssize_t res;
50377fbe694SAlan Somers 	int nready = 0;
5043429092cSAlan Somers 	fd_set readfds;
5053429092cSAlan Somers 	pollfd fds[1];
5063429092cSAlan Somers 	struct kevent changes[1];
5073429092cSAlan Somers 	struct kevent events[1];
50877fbe694SAlan Somers 	struct timespec timeout_ts;
50977fbe694SAlan Somers 	struct timeval timeout_tv;
51077fbe694SAlan Somers 	const int timeout_ms = 999;
51177fbe694SAlan Somers 	int timeout_int, nfds;
5129821f1d3SAlan Somers 
5133429092cSAlan Somers 	switch (m_pm) {
5143429092cSAlan Somers 	case BLOCKING:
5153429092cSAlan Somers 		break;
5163429092cSAlan Somers 	case KQ:
51777fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
51877fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
51977fbe694SAlan Somers 		while (nready == 0) {
52077fbe694SAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
52177fbe694SAlan Somers 				0, 0);
52277fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
52377fbe694SAlan Somers 				&timeout_ts);
5243429092cSAlan Somers 			if (m_quit)
5253429092cSAlan Somers 				return;
52677fbe694SAlan Somers 		}
5273429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
5283429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
5293429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
5303429092cSAlan Somers 			FAIL() << strerror(events[0].data);
5313429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
5323429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
5330a7c63e0SAlan Somers 		m_nready = events[0].data;
5343429092cSAlan Somers 		break;
5353429092cSAlan Somers 	case POLL:
53677fbe694SAlan Somers 		timeout_int = timeout_ms;
5373429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
5383429092cSAlan Somers 		fds[0].events = POLLIN;
53977fbe694SAlan Somers 		while (nready == 0) {
54077fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
5413429092cSAlan Somers 			if (m_quit)
5423429092cSAlan Somers 				return;
54377fbe694SAlan Somers 		}
5443429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
5453429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
5463429092cSAlan Somers 		break;
5473429092cSAlan Somers 	case SELECT:
54877fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
54977fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
55077fbe694SAlan Somers 		nfds = m_fuse_fd + 1;
55177fbe694SAlan Somers 		while (nready == 0) {
5523429092cSAlan Somers 			FD_ZERO(&readfds);
5533429092cSAlan Somers 			FD_SET(m_fuse_fd, &readfds);
55477fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
55577fbe694SAlan Somers 				&timeout_tv);
5563429092cSAlan Somers 			if (m_quit)
5573429092cSAlan Somers 				return;
55877fbe694SAlan Somers 		}
5593429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
5603429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
5613429092cSAlan Somers 		break;
5623429092cSAlan Somers 	default:
5633429092cSAlan Somers 		FAIL() << "not yet implemented";
5643429092cSAlan Somers 	}
56529edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
5663429092cSAlan Somers 
56781a619c4SAlan Somers 	if (res < 0 && !m_quit)
5689821f1d3SAlan Somers 		perror("read");
56929edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
5709821f1d3SAlan Somers }
5719821f1d3SAlan Somers 
57229edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
5733429092cSAlan Somers 	fd_set writefds;
5743429092cSAlan Somers 	pollfd fds[1];
5753429092cSAlan Somers 	int nready, nfds;
5763429092cSAlan Somers 	ssize_t r;
5773429092cSAlan Somers 
5783429092cSAlan Somers 	switch (m_pm) {
5793429092cSAlan Somers 	case BLOCKING:
5803429092cSAlan Somers 	case KQ:	/* EVFILT_WRITE is not supported */
5813429092cSAlan Somers 		break;
5823429092cSAlan Somers 	case POLL:
5833429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
5843429092cSAlan Somers 		fds[0].events = POLLOUT;
5853429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
5863429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
5873429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
5883429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
5893429092cSAlan Somers 		break;
5903429092cSAlan Somers 	case SELECT:
5913429092cSAlan Somers 		FD_ZERO(&writefds);
5923429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
5933429092cSAlan Somers 		nfds = m_fuse_fd + 1;
5943429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
5953429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
5963429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
5973429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
5983429092cSAlan Somers 		break;
5993429092cSAlan Somers 	default:
6003429092cSAlan Somers 		FAIL() << "not yet implemented";
6013429092cSAlan Somers 	}
60229edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
6033429092cSAlan Somers 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
6043429092cSAlan Somers }
6053429092cSAlan Somers 
6069821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
6079821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
6089821f1d3SAlan Somers 
6099821f1d3SAlan Somers 	mock_fs->loop();
6109821f1d3SAlan Somers 
6119821f1d3SAlan Somers 	return (NULL);
6129821f1d3SAlan Somers }
6139821f1d3SAlan Somers 
6149821f1d3SAlan Somers void MockFS::unmount() {
6159821f1d3SAlan Somers 	::unmount("mountpoint", 0);
6169821f1d3SAlan Somers }
617