xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 81a619c4e13fd7aa6d0efdbc6ff61d1c11340142)
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>
359821f1d3SAlan Somers #include <sys/stat.h>
369821f1d3SAlan Somers #include <sys/uio.h>
379821f1d3SAlan Somers #include <sys/user.h>
389821f1d3SAlan Somers 
399821f1d3SAlan Somers #include <fcntl.h>
409821f1d3SAlan Somers #include <libutil.h>
419821f1d3SAlan Somers #include <pthread.h>
429821f1d3SAlan Somers #include <signal.h>
439821f1d3SAlan Somers #include <stdlib.h>
449821f1d3SAlan Somers #include <unistd.h>
459821f1d3SAlan Somers 
469821f1d3SAlan Somers #include "mntopts.h"	// for build_iovec
479821f1d3SAlan Somers }
489821f1d3SAlan Somers 
499821f1d3SAlan Somers #include <gtest/gtest.h>
509821f1d3SAlan Somers 
519821f1d3SAlan Somers #include "mockfs.hh"
529821f1d3SAlan Somers 
539821f1d3SAlan Somers using namespace testing;
549821f1d3SAlan Somers 
559821f1d3SAlan Somers int verbosity = 0;
569821f1d3SAlan Somers 
579821f1d3SAlan Somers const char* opcode2opname(uint32_t opcode)
589821f1d3SAlan Somers {
599821f1d3SAlan Somers 	const int NUM_OPS = 39;
609821f1d3SAlan Somers 	const char* table[NUM_OPS] = {
619821f1d3SAlan Somers 		"Unknown (opcode 0)",
629821f1d3SAlan Somers 		"LOOKUP",
639821f1d3SAlan Somers 		"FORGET",
649821f1d3SAlan Somers 		"GETATTR",
659821f1d3SAlan Somers 		"SETATTR",
669821f1d3SAlan Somers 		"READLINK",
679821f1d3SAlan Somers 		"SYMLINK",
689821f1d3SAlan Somers 		"Unknown (opcode 7)",
699821f1d3SAlan Somers 		"MKNOD",
709821f1d3SAlan Somers 		"MKDIR",
719821f1d3SAlan Somers 		"UNLINK",
729821f1d3SAlan Somers 		"RMDIR",
739821f1d3SAlan Somers 		"RENAME",
749821f1d3SAlan Somers 		"LINK",
759821f1d3SAlan Somers 		"OPEN",
769821f1d3SAlan Somers 		"READ",
779821f1d3SAlan Somers 		"WRITE",
789821f1d3SAlan Somers 		"STATFS",
799821f1d3SAlan Somers 		"RELEASE",
809821f1d3SAlan Somers 		"Unknown (opcode 19)",
819821f1d3SAlan Somers 		"FSYNC",
829821f1d3SAlan Somers 		"SETXATTR",
839821f1d3SAlan Somers 		"GETXATTR",
849821f1d3SAlan Somers 		"LISTXATTR",
859821f1d3SAlan Somers 		"REMOVEXATTR",
869821f1d3SAlan Somers 		"FLUSH",
879821f1d3SAlan Somers 		"INIT",
889821f1d3SAlan Somers 		"OPENDIR",
899821f1d3SAlan Somers 		"READDIR",
909821f1d3SAlan Somers 		"RELEASEDIR",
919821f1d3SAlan Somers 		"FSYNCDIR",
929821f1d3SAlan Somers 		"GETLK",
939821f1d3SAlan Somers 		"SETLK",
949821f1d3SAlan Somers 		"SETLKW",
959821f1d3SAlan Somers 		"ACCESS",
969821f1d3SAlan Somers 		"CREATE",
979821f1d3SAlan Somers 		"INTERRUPT",
989821f1d3SAlan Somers 		"BMAP",
999821f1d3SAlan Somers 		"DESTROY"
1009821f1d3SAlan Somers 	};
1019821f1d3SAlan Somers 	if (opcode >= NUM_OPS)
1029821f1d3SAlan Somers 		return ("Unknown (opcode > max)");
1039821f1d3SAlan Somers 	else
1049821f1d3SAlan Somers 		return (table[opcode]);
1059821f1d3SAlan Somers }
1069821f1d3SAlan Somers 
1079821f1d3SAlan Somers ProcessMockerT
1089821f1d3SAlan Somers ReturnErrno(int error)
1099821f1d3SAlan Somers {
1109821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1119821f1d3SAlan Somers 		auto out0 = new mockfs_buf_out;
1129821f1d3SAlan Somers 		out0->header.unique = in->header.unique;
1139821f1d3SAlan Somers 		out0->header.error = -error;
1149821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
1159821f1d3SAlan Somers 		out.push_back(out0);
1169821f1d3SAlan Somers 	});
1179821f1d3SAlan Somers }
1189821f1d3SAlan Somers 
1199821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
1209821f1d3SAlan Somers ProcessMockerT
1219821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
1229821f1d3SAlan Somers {
1239821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1249821f1d3SAlan Somers 		/* nodeid means ENOENT and cache it */
1259821f1d3SAlan Somers 		auto out0 = new mockfs_buf_out;
1269821f1d3SAlan Somers 		out0->body.entry.nodeid = 0;
1279821f1d3SAlan Somers 		out0->header.unique = in->header.unique;
1289821f1d3SAlan Somers 		out0->header.error = 0;
1299821f1d3SAlan Somers 		out0->body.entry.entry_valid = entry_valid->tv_sec;
1309821f1d3SAlan Somers 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
1319821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out0, entry);
1329821f1d3SAlan Somers 		out.push_back(out0);
1339821f1d3SAlan Somers 	});
1349821f1d3SAlan Somers }
1359821f1d3SAlan Somers 
1369821f1d3SAlan Somers ProcessMockerT
1379821f1d3SAlan Somers ReturnImmediate(std::function<void(const struct mockfs_buf_in *in,
1389821f1d3SAlan Somers 				   struct mockfs_buf_out *out)> f)
1399821f1d3SAlan Somers {
1409821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1419821f1d3SAlan Somers 		auto out0 = new mockfs_buf_out;
1429821f1d3SAlan Somers 		out0->header.unique = in->header.unique;
1439821f1d3SAlan Somers 		f(in, out0);
1449821f1d3SAlan Somers 		out.push_back(out0);
1459821f1d3SAlan Somers 	});
1469821f1d3SAlan Somers }
1479821f1d3SAlan Somers 
1489821f1d3SAlan Somers void sigint_handler(int __unused sig) {
1498b73a4c5SAlan Somers 	// Don't do anything except interrupt the daemon's read(2) call
1509821f1d3SAlan Somers }
1519821f1d3SAlan Somers 
1529821f1d3SAlan Somers void debug_fuseop(const mockfs_buf_in *in)
1539821f1d3SAlan Somers {
1549821f1d3SAlan Somers 	printf("%-11s ino=%2lu", opcode2opname(in->header.opcode),
1559821f1d3SAlan Somers 		in->header.nodeid);
1569821f1d3SAlan Somers 	if (verbosity > 1) {
1579821f1d3SAlan Somers 		printf(" uid=%5u gid=%5u pid=%5u unique=%lu len=%u",
1589821f1d3SAlan Somers 			in->header.uid, in->header.gid, in->header.pid,
1599821f1d3SAlan Somers 			in->header.unique, in->header.len);
1609821f1d3SAlan Somers 	}
1619821f1d3SAlan Somers 	switch (in->header.opcode) {
16219ef317dSAlan Somers 		const char *name, *value;
16319ef317dSAlan Somers 
164caf5f57dSAlan Somers 		case FUSE_ACCESS:
165caf5f57dSAlan Somers 			printf(" mask=%#x", in->body.access.mask);
166caf5f57dSAlan Somers 			break;
16719ef317dSAlan Somers 		case FUSE_CREATE:
16819ef317dSAlan Somers 			name = (const char*)in->body.bytes +
16919ef317dSAlan Somers 				sizeof(fuse_open_in);
17019ef317dSAlan Somers 			printf(" flags=%#x name=%s",
17119ef317dSAlan Somers 				in->body.open.flags, name);
17219ef317dSAlan Somers 			break;
1739821f1d3SAlan Somers 		case FUSE_FLUSH:
1749f10f423SAlan Somers 			printf(" fh=%#lx lock_owner=%lu", in->body.flush.fh,
1759f10f423SAlan Somers 				in->body.flush.lock_owner);
1769821f1d3SAlan Somers 			break;
1779821f1d3SAlan Somers 		case FUSE_FORGET:
1789821f1d3SAlan Somers 			printf(" nlookup=%lu", in->body.forget.nlookup);
1799821f1d3SAlan Somers 			break;
1809821f1d3SAlan Somers 		case FUSE_FSYNC:
1819821f1d3SAlan Somers 			printf(" flags=%#x", in->body.fsync.fsync_flags);
1829821f1d3SAlan Somers 			break;
1839821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
1849821f1d3SAlan Somers 			printf(" flags=%#x", in->body.fsyncdir.fsync_flags);
1859821f1d3SAlan Somers 			break;
186723c7768SAlan Somers 		case FUSE_INTERRUPT:
187723c7768SAlan Somers 			printf(" unique=%lu", in->body.interrupt.unique);
188723c7768SAlan Somers 			break;
189002e54b0SAlan Somers 		case FUSE_LINK:
190002e54b0SAlan Somers 			printf(" oldnodeid=%lu", in->body.link.oldnodeid);
191002e54b0SAlan Somers 			break;
1929821f1d3SAlan Somers 		case FUSE_LOOKUP:
1939821f1d3SAlan Somers 			printf(" %s", in->body.lookup);
1949821f1d3SAlan Somers 			break;
195bf4d7084SAlan Somers 		case FUSE_MKNOD:
196bf4d7084SAlan Somers 			printf(" mode=%#o rdev=%x", in->body.mknod.mode,
197bf4d7084SAlan Somers 				in->body.mknod.rdev);
198bf4d7084SAlan Somers 			break;
1999821f1d3SAlan Somers 		case FUSE_OPEN:
2009821f1d3SAlan Somers 			printf(" flags=%#x mode=%#o",
2019821f1d3SAlan Somers 				in->body.open.flags, in->body.open.mode);
2029821f1d3SAlan Somers 			break;
2039821f1d3SAlan Somers 		case FUSE_OPENDIR:
2049821f1d3SAlan Somers 			printf(" flags=%#x mode=%#o",
2059821f1d3SAlan Somers 				in->body.opendir.flags, in->body.opendir.mode);
2069821f1d3SAlan Somers 			break;
2079821f1d3SAlan Somers 		case FUSE_READ:
2089821f1d3SAlan Somers 			printf(" offset=%lu size=%u", in->body.read.offset,
2099821f1d3SAlan Somers 				in->body.read.size);
2109821f1d3SAlan Somers 			break;
2119821f1d3SAlan Somers 		case FUSE_READDIR:
212363a7416SAlan Somers 			printf(" fh=%#lx offset=%lu size=%u",
213363a7416SAlan Somers 				in->body.readdir.fh, in->body.readdir.offset,
2149821f1d3SAlan Somers 				in->body.readdir.size);
2159821f1d3SAlan Somers 			break;
2169821f1d3SAlan Somers 		case FUSE_RELEASE:
21742d50d16SAlan Somers 			printf(" fh=%#lx flags=%#x lock_owner=%lu",
21842d50d16SAlan Somers 				in->body.release.fh,
2199821f1d3SAlan Somers 				in->body.release.flags,
2209821f1d3SAlan Somers 				in->body.release.lock_owner);
2219821f1d3SAlan Somers 			break;
2229821f1d3SAlan Somers 		case FUSE_SETATTR:
2239821f1d3SAlan Somers 			if (verbosity <= 1) {
2249821f1d3SAlan Somers 				printf(" valid=%#x", in->body.setattr.valid);
2259821f1d3SAlan Somers 				break;
2269821f1d3SAlan Somers 			}
2279821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_MODE)
2289821f1d3SAlan Somers 				printf(" mode=%#o", in->body.setattr.mode);
2299821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_UID)
2309821f1d3SAlan Somers 				printf(" uid=%u", in->body.setattr.uid);
2319821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_GID)
2329821f1d3SAlan Somers 				printf(" gid=%u", in->body.setattr.gid);
2339821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_SIZE)
2349821f1d3SAlan Somers 				printf(" size=%zu", in->body.setattr.size);
2359821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_ATIME)
2369821f1d3SAlan Somers 				printf(" atime=%zu.%u",
2379821f1d3SAlan Somers 					in->body.setattr.atime,
2389821f1d3SAlan Somers 					in->body.setattr.atimensec);
2399821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_MTIME)
2409821f1d3SAlan Somers 				printf(" mtime=%zu.%u",
2419821f1d3SAlan Somers 					in->body.setattr.mtime,
2429821f1d3SAlan Somers 					in->body.setattr.mtimensec);
2439821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_FH)
2449821f1d3SAlan Somers 				printf(" fh=%zu", in->body.setattr.fh);
2459821f1d3SAlan Somers 			break;
246f067b609SAlan Somers 		case FUSE_SETLK:
247f067b609SAlan Somers 			printf(" fh=%#lx owner=%lu type=%u pid=%u",
248f067b609SAlan Somers 				in->body.setlk.fh, in->body.setlk.owner,
249f067b609SAlan Somers 				in->body.setlk.lk.type,
250f067b609SAlan Somers 				in->body.setlk.lk.pid);
251f067b609SAlan Somers 			if (verbosity >= 2) {
252f067b609SAlan Somers 				printf(" range=[%lu-%lu]",
253f067b609SAlan Somers 					in->body.setlk.lk.start,
254f067b609SAlan Somers 					in->body.setlk.lk.end);
255f067b609SAlan Somers 			}
256f067b609SAlan Somers 			break;
2579821f1d3SAlan Somers 		case FUSE_SETXATTR:
2589821f1d3SAlan Somers 			/*
2599821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
2609821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
2619821f1d3SAlan Somers 			 */
26219ef317dSAlan Somers 			name = (const char*)in->body.bytes +
2639821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
26419ef317dSAlan Somers 			value = name + strlen(name) + 1;
26519ef317dSAlan Somers 			printf(" %s=%s", name, value);
2669821f1d3SAlan Somers 			break;
2679821f1d3SAlan Somers 		case FUSE_WRITE:
26812292a99SAlan Somers 			printf(" fh=%#lx offset=%lu size=%u flags=%u",
26912292a99SAlan Somers 				in->body.write.fh,
2709821f1d3SAlan Somers 				in->body.write.offset, in->body.write.size,
2719821f1d3SAlan Somers 				in->body.write.write_flags);
2729821f1d3SAlan Somers 			break;
2739821f1d3SAlan Somers 		default:
2749821f1d3SAlan Somers 			break;
2759821f1d3SAlan Somers 	}
2769821f1d3SAlan Somers 	printf("\n");
2779821f1d3SAlan Somers }
2789821f1d3SAlan Somers 
27991ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
280140bb492SAlan Somers 	bool push_symlinks_in, bool ro, uint32_t flags)
2819821f1d3SAlan Somers {
2828b73a4c5SAlan Somers 	struct sigaction sa;
2839821f1d3SAlan Somers 	struct iovec *iov = NULL;
2849821f1d3SAlan Somers 	int iovlen = 0;
2859821f1d3SAlan Somers 	char fdstr[15];
28691ff3a0dSAlan Somers 	const bool trueval = true;
2879821f1d3SAlan Somers 
2889821f1d3SAlan Somers 	m_daemon_id = NULL;
2899821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
290*81a619c4SAlan Somers 	m_quit = false;
2919821f1d3SAlan Somers 
2929821f1d3SAlan Somers 	/*
2939821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
2949821f1d3SAlan Somers 	 * mkdtemp
2959821f1d3SAlan Somers 	 */
2969821f1d3SAlan Somers 	/*
2979821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
2989821f1d3SAlan Somers 	 * instead.
2999821f1d3SAlan Somers 	 */
30091ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
3019821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3029821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
3039821f1d3SAlan Somers 
30491ff3a0dSAlan Somers 	m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
3059821f1d3SAlan Somers 	if (m_fuse_fd < 0)
3069821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3079821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
3089821f1d3SAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
3099821f1d3SAlan Somers 
3109821f1d3SAlan Somers 	m_pid = getpid();
31191ff3a0dSAlan Somers 	m_child_pid = -1;
3129821f1d3SAlan Somers 
3139821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
3149821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
3159821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
3169821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
3179821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
31891ff3a0dSAlan Somers 	if (allow_other) {
31991ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
3209821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
3219821f1d3SAlan Somers 	}
3229821f1d3SAlan Somers 	if (default_permissions) {
3239821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
3249821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
3259821f1d3SAlan Somers 	}
32691ff3a0dSAlan Somers 	if (push_symlinks_in) {
32791ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
32891ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
32991ff3a0dSAlan Somers 	}
330140bb492SAlan Somers 	if (ro) {
331140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
332140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
333140bb492SAlan Somers 	}
3349821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
3359821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3369821f1d3SAlan Somers 			"Couldn't mount filesystem"));
3379821f1d3SAlan Somers 
3389821f1d3SAlan Somers 	// Setup default handler
3399821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
3409821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
3419821f1d3SAlan Somers 
3429821f1d3SAlan Somers 	init(flags);
3438b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
3448b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
3458b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
3468b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
3478b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3488b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
3499821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
3509821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3519821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
3529821f1d3SAlan Somers }
3539821f1d3SAlan Somers 
3549821f1d3SAlan Somers MockFS::~MockFS() {
3559821f1d3SAlan Somers 	kill_daemon();
3569821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
3579821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
3589821f1d3SAlan Somers 		m_daemon_id = NULL;
3599821f1d3SAlan Somers 	}
3608b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
3619821f1d3SAlan Somers 	rmdir("mountpoint");
3629821f1d3SAlan Somers }
3639821f1d3SAlan Somers 
3649821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
3659821f1d3SAlan Somers 	mockfs_buf_in *in;
3669821f1d3SAlan Somers 	mockfs_buf_out *out;
3679821f1d3SAlan Somers 
3689821f1d3SAlan Somers 	in = (mockfs_buf_in*) malloc(sizeof(*in));
3699821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
3709821f1d3SAlan Somers 	out = (mockfs_buf_out*) malloc(sizeof(*out));
3719821f1d3SAlan Somers 	ASSERT_TRUE(out != NULL);
3729821f1d3SAlan Somers 
3739821f1d3SAlan Somers 	read_request(in);
3749821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
3759821f1d3SAlan Somers 
3769821f1d3SAlan Somers 	memset(out, 0, sizeof(*out));
3779821f1d3SAlan Somers 	out->header.unique = in->header.unique;
3789821f1d3SAlan Somers 	out->header.error = 0;
3799821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
3809821f1d3SAlan Somers 	out->body.init.minor = FUSE_KERNEL_MINOR_VERSION;
3819821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
3829821f1d3SAlan Somers 
3839821f1d3SAlan Somers 	/*
3849821f1d3SAlan Somers 	 * The default max_write is set to this formula in libfuse, though
3859821f1d3SAlan Somers 	 * individual filesystems can lower it.  The "- 4096" was added in
3869821f1d3SAlan Somers 	 * commit 154ffe2, with the commit message "fix".
3879821f1d3SAlan Somers 	 */
3889821f1d3SAlan Somers 	uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096;
3899821f1d3SAlan Somers 	/* For testing purposes, it should be distinct from MAXPHYS */
3909821f1d3SAlan Somers 	m_max_write = MIN(default_max_write, MAXPHYS / 2);
3919821f1d3SAlan Somers 	out->body.init.max_write = m_max_write;
3929821f1d3SAlan Somers 
3939821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
3949821f1d3SAlan Somers 	SET_OUT_HEADER_LEN(out, init);
3959821f1d3SAlan Somers 	write(m_fuse_fd, out, out->header.len);
3969821f1d3SAlan Somers 
3979821f1d3SAlan Somers 	free(in);
3989821f1d3SAlan Somers }
3999821f1d3SAlan Somers 
4009821f1d3SAlan Somers void MockFS::kill_daemon() {
401*81a619c4SAlan Somers 	m_quit = true;
4028b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
4039821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
4048b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
4058b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
4068b73a4c5SAlan Somers 	// during the unmount sequence.
4079821f1d3SAlan Somers 	close(m_fuse_fd);
4088b73a4c5SAlan Somers 	m_fuse_fd = -1;
4099821f1d3SAlan Somers }
4109821f1d3SAlan Somers 
4119821f1d3SAlan Somers void MockFS::loop() {
4129821f1d3SAlan Somers 	mockfs_buf_in *in;
4139821f1d3SAlan Somers 	std::vector<mockfs_buf_out*> out;
4149821f1d3SAlan Somers 
4159821f1d3SAlan Somers 	in = (mockfs_buf_in*) malloc(sizeof(*in));
4169821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
417*81a619c4SAlan Somers 	while (!m_quit) {
4189821f1d3SAlan Somers 		bzero(in, sizeof(*in));
4199821f1d3SAlan Somers 		read_request(in);
420*81a619c4SAlan Somers 		if (m_quit)
4219821f1d3SAlan Somers 			break;
4229821f1d3SAlan Somers 		if (verbosity > 0)
4239821f1d3SAlan Somers 			debug_fuseop(in);
4249821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
4259821f1d3SAlan Somers 			process(in, out);
4269821f1d3SAlan Somers 		} else {
4279821f1d3SAlan Somers 			/*
4289821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
4299821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
4309821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
4319821f1d3SAlan Somers 			 */
4329821f1d3SAlan Somers 			process_default(in, out);
4339821f1d3SAlan Somers 		}
4349821f1d3SAlan Somers 		for (auto &it: out) {
4359821f1d3SAlan Somers 			ASSERT_TRUE(write(m_fuse_fd, it, it->header.len) > 0 ||
4369821f1d3SAlan Somers 				    errno == EAGAIN)
4379821f1d3SAlan Somers 				<< strerror(errno);
4389821f1d3SAlan Somers 			delete it;
4399821f1d3SAlan Somers 		}
4409821f1d3SAlan Somers 		out.clear();
4419821f1d3SAlan Somers 	}
4429821f1d3SAlan Somers 	free(in);
4439821f1d3SAlan Somers }
4449821f1d3SAlan Somers 
4459821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
4469821f1d3SAlan Somers 	if (pid == m_pid) {
4479821f1d3SAlan Somers 		return (true);
44891ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
44991ff3a0dSAlan Somers 		return (true);
4509821f1d3SAlan Somers 	} else {
4519821f1d3SAlan Somers 		struct kinfo_proc *ki;
4529821f1d3SAlan Somers 		bool ok = false;
4539821f1d3SAlan Somers 
4549821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
4559821f1d3SAlan Somers 		if (ki == NULL)
4569821f1d3SAlan Somers 			return (false);
4579821f1d3SAlan Somers 		/*
4589821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
4599821f1d3SAlan Somers 		 * can use aio functions
4609821f1d3SAlan Somers 		 */
4619821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
4629821f1d3SAlan Somers 			ok = true;
4639821f1d3SAlan Somers 		free(ki);
4649821f1d3SAlan Somers 		return (ok);
4659821f1d3SAlan Somers 	}
4669821f1d3SAlan Somers }
4679821f1d3SAlan Somers 
4689821f1d3SAlan Somers void MockFS::process_default(const mockfs_buf_in *in,
4699821f1d3SAlan Somers 		std::vector<mockfs_buf_out*> &out)
4709821f1d3SAlan Somers {
4718b73a4c5SAlan Somers 	if (verbosity > 1)
4728b73a4c5SAlan Somers 		printf("%-11s REJECTED (wrong pid %d)\n",
4738b73a4c5SAlan Somers 			opcode2opname(in->header.opcode), in->header.pid);
4749821f1d3SAlan Somers 	auto out0 = new mockfs_buf_out;
4759821f1d3SAlan Somers 	out0->header.unique = in->header.unique;
4769821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
4779821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
4789821f1d3SAlan Somers 	out.push_back(out0);
4799821f1d3SAlan Somers }
4809821f1d3SAlan Somers 
4819821f1d3SAlan Somers void MockFS::read_request(mockfs_buf_in *in) {
4829821f1d3SAlan Somers 	ssize_t res;
4839821f1d3SAlan Somers 
4849821f1d3SAlan Somers 	res = read(m_fuse_fd, in, sizeof(*in));
485*81a619c4SAlan Somers 	if (res < 0 && !m_quit)
4869821f1d3SAlan Somers 		perror("read");
487*81a619c4SAlan Somers 	ASSERT_TRUE(res >= (ssize_t)sizeof(in->header) || m_quit);
4889821f1d3SAlan Somers }
4899821f1d3SAlan Somers 
4909821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
4919821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
4929821f1d3SAlan Somers 
4939821f1d3SAlan Somers 	mock_fs->loop();
4949821f1d3SAlan Somers 
4959821f1d3SAlan Somers 	return (NULL);
4969821f1d3SAlan Somers }
4979821f1d3SAlan Somers 
4989821f1d3SAlan Somers void MockFS::unmount() {
4999821f1d3SAlan Somers 	::unmount("mountpoint", 0);
5009821f1d3SAlan Somers }
501