xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 8e7657374aa215ec065d2f76753d799136a3c0cb)
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.
291fa8ebfbSAlan Somers  *
301fa8ebfbSAlan Somers  * $FreeBSD$
319821f1d3SAlan Somers  */
329821f1d3SAlan Somers 
339821f1d3SAlan Somers extern "C" {
349821f1d3SAlan Somers #include <sys/param.h>
359821f1d3SAlan Somers 
369821f1d3SAlan Somers #include <sys/mount.h>
373429092cSAlan Somers #include <sys/select.h>
389821f1d3SAlan Somers #include <sys/stat.h>
399821f1d3SAlan Somers #include <sys/uio.h>
409821f1d3SAlan Somers #include <sys/user.h>
419821f1d3SAlan Somers 
429821f1d3SAlan Somers #include <fcntl.h>
439821f1d3SAlan Somers #include <libutil.h>
443429092cSAlan Somers #include <poll.h>
459821f1d3SAlan Somers #include <pthread.h>
469821f1d3SAlan Somers #include <signal.h>
479821f1d3SAlan Somers #include <stdlib.h>
489821f1d3SAlan Somers #include <unistd.h>
499821f1d3SAlan Somers 
509821f1d3SAlan Somers #include "mntopts.h"	// for build_iovec
519821f1d3SAlan Somers }
529821f1d3SAlan Somers 
53cc04566cSAlan Somers #include <cinttypes>
54cc04566cSAlan Somers 
559821f1d3SAlan Somers #include <gtest/gtest.h>
569821f1d3SAlan Somers 
579821f1d3SAlan Somers #include "mockfs.hh"
589821f1d3SAlan Somers 
599821f1d3SAlan Somers using namespace testing;
609821f1d3SAlan Somers 
619821f1d3SAlan Somers int verbosity = 0;
629821f1d3SAlan Somers 
639821f1d3SAlan Somers const char* opcode2opname(uint32_t opcode)
649821f1d3SAlan Somers {
659821f1d3SAlan Somers 	const int NUM_OPS = 39;
669821f1d3SAlan Somers 	const char* table[NUM_OPS] = {
679821f1d3SAlan Somers 		"Unknown (opcode 0)",
689821f1d3SAlan Somers 		"LOOKUP",
699821f1d3SAlan Somers 		"FORGET",
709821f1d3SAlan Somers 		"GETATTR",
719821f1d3SAlan Somers 		"SETATTR",
729821f1d3SAlan Somers 		"READLINK",
739821f1d3SAlan Somers 		"SYMLINK",
749821f1d3SAlan Somers 		"Unknown (opcode 7)",
759821f1d3SAlan Somers 		"MKNOD",
769821f1d3SAlan Somers 		"MKDIR",
779821f1d3SAlan Somers 		"UNLINK",
789821f1d3SAlan Somers 		"RMDIR",
799821f1d3SAlan Somers 		"RENAME",
809821f1d3SAlan Somers 		"LINK",
819821f1d3SAlan Somers 		"OPEN",
829821f1d3SAlan Somers 		"READ",
839821f1d3SAlan Somers 		"WRITE",
849821f1d3SAlan Somers 		"STATFS",
859821f1d3SAlan Somers 		"RELEASE",
869821f1d3SAlan Somers 		"Unknown (opcode 19)",
879821f1d3SAlan Somers 		"FSYNC",
889821f1d3SAlan Somers 		"SETXATTR",
899821f1d3SAlan Somers 		"GETXATTR",
909821f1d3SAlan Somers 		"LISTXATTR",
919821f1d3SAlan Somers 		"REMOVEXATTR",
929821f1d3SAlan Somers 		"FLUSH",
939821f1d3SAlan Somers 		"INIT",
949821f1d3SAlan Somers 		"OPENDIR",
959821f1d3SAlan Somers 		"READDIR",
969821f1d3SAlan Somers 		"RELEASEDIR",
979821f1d3SAlan Somers 		"FSYNCDIR",
989821f1d3SAlan Somers 		"GETLK",
999821f1d3SAlan Somers 		"SETLK",
1009821f1d3SAlan Somers 		"SETLKW",
1019821f1d3SAlan Somers 		"ACCESS",
1029821f1d3SAlan Somers 		"CREATE",
1039821f1d3SAlan Somers 		"INTERRUPT",
1049821f1d3SAlan Somers 		"BMAP",
1059821f1d3SAlan Somers 		"DESTROY"
1069821f1d3SAlan Somers 	};
1079821f1d3SAlan Somers 	if (opcode >= NUM_OPS)
1089821f1d3SAlan Somers 		return ("Unknown (opcode > max)");
1099821f1d3SAlan Somers 	else
1109821f1d3SAlan Somers 		return (table[opcode]);
1119821f1d3SAlan Somers }
1129821f1d3SAlan Somers 
1139821f1d3SAlan Somers ProcessMockerT
1149821f1d3SAlan Somers ReturnErrno(int error)
1159821f1d3SAlan Somers {
1169821f1d3SAlan Somers 	return([=](auto in, auto &out) {
11729edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
11829edc611SAlan Somers 		out0->header.unique = in.header.unique;
1199821f1d3SAlan Somers 		out0->header.error = -error;
1209821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
12129edc611SAlan Somers 		out.push_back(std::move(out0));
1229821f1d3SAlan Somers 	});
1239821f1d3SAlan Somers }
1249821f1d3SAlan Somers 
1259821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
1269821f1d3SAlan Somers ProcessMockerT
1279821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
1289821f1d3SAlan Somers {
1299821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1309821f1d3SAlan Somers 		/* nodeid means ENOENT and cache it */
13129edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
1329821f1d3SAlan Somers 		out0->body.entry.nodeid = 0;
13329edc611SAlan Somers 		out0->header.unique = in.header.unique;
1349821f1d3SAlan Somers 		out0->header.error = 0;
1359821f1d3SAlan Somers 		out0->body.entry.entry_valid = entry_valid->tv_sec;
1369821f1d3SAlan Somers 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
13729edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
13829edc611SAlan Somers 		out.push_back(std::move(out0));
1399821f1d3SAlan Somers 	});
1409821f1d3SAlan Somers }
1419821f1d3SAlan Somers 
1429821f1d3SAlan Somers ProcessMockerT
14329edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in,
14429edc611SAlan Somers 				   struct mockfs_buf_out &out)> f)
1459821f1d3SAlan Somers {
14629edc611SAlan Somers 	return([=](auto& in, auto &out) {
14729edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
14829edc611SAlan Somers 		out0->header.unique = in.header.unique;
14929edc611SAlan Somers 		f(in, *out0);
15029edc611SAlan Somers 		out.push_back(std::move(out0));
1519821f1d3SAlan Somers 	});
1529821f1d3SAlan Somers }
1539821f1d3SAlan Somers 
1549821f1d3SAlan Somers void sigint_handler(int __unused sig) {
1558b73a4c5SAlan Somers 	// Don't do anything except interrupt the daemon's read(2) call
1569821f1d3SAlan Somers }
1579821f1d3SAlan Somers 
158c2d70d6eSAlan Somers void MockFS::debug_request(const mockfs_buf_in &in)
1599821f1d3SAlan Somers {
16029edc611SAlan Somers 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
16129edc611SAlan Somers 		in.header.nodeid);
1629821f1d3SAlan Somers 	if (verbosity > 1) {
163cc04566cSAlan Somers 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u",
16429edc611SAlan Somers 			in.header.uid, in.header.gid, in.header.pid,
16529edc611SAlan Somers 			in.header.unique, in.header.len);
1669821f1d3SAlan Somers 	}
16729edc611SAlan Somers 	switch (in.header.opcode) {
16819ef317dSAlan Somers 		const char *name, *value;
16919ef317dSAlan Somers 
170caf5f57dSAlan Somers 		case FUSE_ACCESS:
17129edc611SAlan Somers 			printf(" mask=%#x", in.body.access.mask);
172caf5f57dSAlan Somers 			break;
173a1c9f4adSAlan Somers 		case FUSE_BMAP:
17497b0512bSAlan Somers 			printf(" block=%" PRIx64 " blocksize=%#x",
17597b0512bSAlan Somers 				in.body.bmap.block, in.body.bmap.blocksize);
176a1c9f4adSAlan Somers 			break;
17719ef317dSAlan Somers 		case FUSE_CREATE:
178a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
179a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
180a4856c96SAlan Somers 					sizeof(fuse_create_in);
181a4856c96SAlan Somers 			else
18229edc611SAlan Somers 				name = (const char*)in.body.bytes +
18319ef317dSAlan Somers 					sizeof(fuse_open_in);
18419ef317dSAlan Somers 			printf(" flags=%#x name=%s",
18529edc611SAlan Somers 				in.body.open.flags, name);
18619ef317dSAlan Somers 			break;
1879821f1d3SAlan Somers 		case FUSE_FLUSH:
188cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
18929edc611SAlan Somers 				in.body.flush.fh,
19029edc611SAlan Somers 				in.body.flush.lock_owner);
1919821f1d3SAlan Somers 			break;
1929821f1d3SAlan Somers 		case FUSE_FORGET:
19329edc611SAlan Somers 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
1949821f1d3SAlan Somers 			break;
1959821f1d3SAlan Somers 		case FUSE_FSYNC:
19629edc611SAlan Somers 			printf(" flags=%#x", in.body.fsync.fsync_flags);
1979821f1d3SAlan Somers 			break;
1989821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
19929edc611SAlan Somers 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
2009821f1d3SAlan Somers 			break;
201723c7768SAlan Somers 		case FUSE_INTERRUPT:
20229edc611SAlan Somers 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
203723c7768SAlan Somers 			break;
204002e54b0SAlan Somers 		case FUSE_LINK:
20529edc611SAlan Somers 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
206002e54b0SAlan Somers 			break;
2075e633330SAlan Somers 		case FUSE_LISTXATTR:
2085e633330SAlan Somers 			printf(" size=%" PRIu32, in.body.listxattr.size);
2095e633330SAlan Somers 			break;
2109821f1d3SAlan Somers 		case FUSE_LOOKUP:
21129edc611SAlan Somers 			printf(" %s", in.body.lookup);
2129821f1d3SAlan Somers 			break;
21399cf7bffSAlan Somers 		case FUSE_MKDIR:
21429edc611SAlan Somers 			name = (const char*)in.body.bytes +
21599cf7bffSAlan Somers 				sizeof(fuse_mkdir_in);
216a4856c96SAlan Somers 			printf(" name=%s mode=%#o umask=%#o", name,
217a4856c96SAlan Somers 				in.body.mkdir.mode, in.body.mkdir.umask);
21899cf7bffSAlan Somers 			break;
219bf4d7084SAlan Somers 		case FUSE_MKNOD:
220a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
221a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
222a4856c96SAlan Somers 					sizeof(fuse_mknod_in);
223a4856c96SAlan Somers 			else
224a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
225a4856c96SAlan Somers 					FUSE_COMPAT_MKNOD_IN_SIZE;
226a4856c96SAlan Somers 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
227a4856c96SAlan Somers 				in.body.mknod.mode, in.body.mknod.rdev,
228a4856c96SAlan Somers 				in.body.mknod.umask, name);
229bf4d7084SAlan Somers 			break;
2309821f1d3SAlan Somers 		case FUSE_OPEN:
231a4856c96SAlan Somers 			printf(" flags=%#x", in.body.open.flags);
2329821f1d3SAlan Somers 			break;
2339821f1d3SAlan Somers 		case FUSE_OPENDIR:
234a4856c96SAlan Somers 			printf(" flags=%#x", in.body.opendir.flags);
2359821f1d3SAlan Somers 			break;
2369821f1d3SAlan Somers 		case FUSE_READ:
237cc04566cSAlan Somers 			printf(" offset=%" PRIu64 " size=%u",
23829edc611SAlan Somers 				in.body.read.offset,
23929edc611SAlan Somers 				in.body.read.size);
240d4fd0c81SAlan Somers 			if (verbosity > 1)
241d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.read.flags);
2429821f1d3SAlan Somers 			break;
2439821f1d3SAlan Somers 		case FUSE_READDIR:
244cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
24529edc611SAlan Somers 				in.body.readdir.fh, in.body.readdir.offset,
24629edc611SAlan Somers 				in.body.readdir.size);
2479821f1d3SAlan Somers 			break;
2489821f1d3SAlan Somers 		case FUSE_RELEASE:
249cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
25029edc611SAlan Somers 				in.body.release.fh,
25129edc611SAlan Somers 				in.body.release.flags,
25229edc611SAlan Somers 				in.body.release.lock_owner);
2539821f1d3SAlan Somers 			break;
2549821f1d3SAlan Somers 		case FUSE_SETATTR:
2559821f1d3SAlan Somers 			if (verbosity <= 1) {
25629edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
2579821f1d3SAlan Somers 				break;
2589821f1d3SAlan Somers 			}
25929edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
26029edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
26129edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
26229edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
26329edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
26429edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
26529edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
26629edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
26729edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
268cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
26929edc611SAlan Somers 					in.body.setattr.atime,
27029edc611SAlan Somers 					in.body.setattr.atimensec);
27129edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
272cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
27329edc611SAlan Somers 					in.body.setattr.mtime,
27429edc611SAlan Somers 					in.body.setattr.mtimensec);
27529edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
27629edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
2779821f1d3SAlan Somers 			break;
278f067b609SAlan Somers 		case FUSE_SETLK:
279cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
280cc04566cSAlan Somers 				" type=%u pid=%u",
28129edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
28229edc611SAlan Somers 				in.body.setlk.lk.type,
28329edc611SAlan Somers 				in.body.setlk.lk.pid);
284f067b609SAlan Somers 			if (verbosity >= 2) {
285cc04566cSAlan Somers 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
28629edc611SAlan Somers 					in.body.setlk.lk.start,
28729edc611SAlan Somers 					in.body.setlk.lk.end);
288f067b609SAlan Somers 			}
289f067b609SAlan Somers 			break;
2909821f1d3SAlan Somers 		case FUSE_SETXATTR:
2919821f1d3SAlan Somers 			/*
2929821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
2939821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
2949821f1d3SAlan Somers 			 */
29529edc611SAlan Somers 			name = (const char*)in.body.bytes +
2969821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
29719ef317dSAlan Somers 			value = name + strlen(name) + 1;
29819ef317dSAlan Somers 			printf(" %s=%s", name, value);
2999821f1d3SAlan Somers 			break;
3009821f1d3SAlan Somers 		case FUSE_WRITE:
301cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
302d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
30329edc611SAlan Somers 				in.body.write.fh,
30429edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
30529edc611SAlan Somers 				in.body.write.write_flags);
306d4fd0c81SAlan Somers 			if (verbosity > 1)
307d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
3089821f1d3SAlan Somers 			break;
3099821f1d3SAlan Somers 		default:
3109821f1d3SAlan Somers 			break;
3119821f1d3SAlan Somers 	}
3129821f1d3SAlan Somers 	printf("\n");
3139821f1d3SAlan Somers }
3149821f1d3SAlan Somers 
315c2d70d6eSAlan Somers /*
316c2d70d6eSAlan Somers  * Debug a FUSE response.
317c2d70d6eSAlan Somers  *
318c2d70d6eSAlan Somers  * This is mostly useful for asynchronous notifications, which don't correspond
319c2d70d6eSAlan Somers  * to any request
320c2d70d6eSAlan Somers  */
321c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
322c2d70d6eSAlan Somers 	const char *name;
323c2d70d6eSAlan Somers 
324c2d70d6eSAlan Somers 	if (verbosity == 0)
325c2d70d6eSAlan Somers 		return;
326c2d70d6eSAlan Somers 
327c2d70d6eSAlan Somers 	switch (out.header.error) {
328c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
329c2d70d6eSAlan Somers 			name = (const char*)out.body.bytes +
330c2d70d6eSAlan Somers 				sizeof(fuse_notify_inval_entry_out);
331c2d70d6eSAlan Somers 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
332c2d70d6eSAlan Somers 				out.body.inval_entry.parent, name);
333c2d70d6eSAlan Somers 			break;
334eae1ae13SAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
335eae1ae13SAlan Somers 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
336eae1ae13SAlan Somers 				" len=%" PRIi64 "\n",
337eae1ae13SAlan Somers 				out.body.inval_inode.ino,
338eae1ae13SAlan Somers 				out.body.inval_inode.off,
339eae1ae13SAlan Somers 				out.body.inval_inode.len);
340eae1ae13SAlan Somers 			break;
3417cbb8e8aSAlan Somers 		case FUSE_NOTIFY_STORE:
3427cbb8e8aSAlan Somers 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
3437cbb8e8aSAlan Somers 				" size=%" PRIu32 "\n",
3447cbb8e8aSAlan Somers 				out.body.store.nodeid,
3457cbb8e8aSAlan Somers 				out.body.store.offset,
3467cbb8e8aSAlan Somers 				out.body.store.size);
3477cbb8e8aSAlan Somers 			break;
348c2d70d6eSAlan Somers 		default:
349c2d70d6eSAlan Somers 			break;
350c2d70d6eSAlan Somers 	}
351c2d70d6eSAlan Somers }
352c2d70d6eSAlan Somers 
35391ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
35416bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
355402b609cSAlan Somers 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
356ed74f781SAlan Somers 	bool noclusterr, unsigned time_gran, bool nointr)
3579821f1d3SAlan Somers {
3588b73a4c5SAlan Somers 	struct sigaction sa;
3599821f1d3SAlan Somers 	struct iovec *iov = NULL;
3609821f1d3SAlan Somers 	int iovlen = 0;
3619821f1d3SAlan Somers 	char fdstr[15];
36291ff3a0dSAlan Somers 	const bool trueval = true;
3639821f1d3SAlan Somers 
3649821f1d3SAlan Somers 	m_daemon_id = NULL;
36516bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
3669821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
3678eecd9ceSAlan Somers 	m_maxwrite = max_write;
3680a7c63e0SAlan Somers 	m_nready = -1;
3693429092cSAlan Somers 	m_pm = pm;
370fef46454SAlan Somers 	m_time_gran = time_gran;
37181a619c4SAlan Somers 	m_quit = false;
3723429092cSAlan Somers 	if (m_pm == KQ)
3733429092cSAlan Somers 		m_kq = kqueue();
3743429092cSAlan Somers 	else
3753429092cSAlan Somers 		m_kq = -1;
3769821f1d3SAlan Somers 
3779821f1d3SAlan Somers 	/*
3789821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
3799821f1d3SAlan Somers 	 * mkdtemp
3809821f1d3SAlan Somers 	 */
3819821f1d3SAlan Somers 	/*
3829821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
3839821f1d3SAlan Somers 	 * instead.
3849821f1d3SAlan Somers 	 */
38591ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
3869821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3879821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
3889821f1d3SAlan Somers 
3893429092cSAlan Somers 	switch (m_pm) {
3903429092cSAlan Somers 	case BLOCKING:
39191ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
3923429092cSAlan Somers 		break;
3933429092cSAlan Somers 	default:
3943429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
3953429092cSAlan Somers 		break;
3963429092cSAlan Somers 	}
3979821f1d3SAlan Somers 	if (m_fuse_fd < 0)
3989821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3999821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
4009821f1d3SAlan Somers 
4019821f1d3SAlan Somers 	m_pid = getpid();
40291ff3a0dSAlan Somers 	m_child_pid = -1;
4039821f1d3SAlan Somers 
4049821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4059821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
4069821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
4079821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4083429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
4099821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
41091ff3a0dSAlan Somers 	if (allow_other) {
41191ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
4129821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4139821f1d3SAlan Somers 	}
4149821f1d3SAlan Somers 	if (default_permissions) {
4159821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
4169821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4179821f1d3SAlan Somers 	}
41891ff3a0dSAlan Somers 	if (push_symlinks_in) {
41991ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
42091ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
42191ff3a0dSAlan Somers 	}
422140bb492SAlan Somers 	if (ro) {
423140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
424140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
425140bb492SAlan Somers 	}
4268eecd9ceSAlan Somers 	if (async) {
4278eecd9ceSAlan Somers 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4288eecd9ceSAlan Somers 			sizeof(bool));
4298eecd9ceSAlan Somers 	}
430402b609cSAlan Somers 	if (noclusterr) {
431402b609cSAlan Somers 		build_iovec(&iov, &iovlen, "noclusterr",
432402b609cSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
433402b609cSAlan Somers 	}
434ed74f781SAlan Somers 	if (nointr) {
435ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "nointr",
436ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
437ed74f781SAlan Somers 	} else {
438ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "intr",
439ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
440ed74f781SAlan Somers 	}
4419821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
4429821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4439821f1d3SAlan Somers 			"Couldn't mount filesystem"));
4449821f1d3SAlan Somers 
4459821f1d3SAlan Somers 	// Setup default handler
4469821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
4479821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
4489821f1d3SAlan Somers 
4499821f1d3SAlan Somers 	init(flags);
4508b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
4518b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
4528b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
4538b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
4548b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4558b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
4569821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
4579821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4589821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
4599821f1d3SAlan Somers }
4609821f1d3SAlan Somers 
4619821f1d3SAlan Somers MockFS::~MockFS() {
4629821f1d3SAlan Somers 	kill_daemon();
4639821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
4649821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
4659821f1d3SAlan Somers 		m_daemon_id = NULL;
4669821f1d3SAlan Somers 	}
4678b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
4689821f1d3SAlan Somers 	rmdir("mountpoint");
4693429092cSAlan Somers 	if (m_kq >= 0)
4703429092cSAlan Somers 		close(m_kq);
4719821f1d3SAlan Somers }
4729821f1d3SAlan Somers 
473bf507497SAlan Somers void MockFS::audit_request(const mockfs_buf_in &in) {
474bf507497SAlan Somers 	uint32_t inlen = in.header.len;
475bf507497SAlan Somers 	size_t fih = sizeof(in.header);
476bf507497SAlan Somers 	switch (in.header.opcode) {
477bf507497SAlan Somers 	case FUSE_LOOKUP:
478bf507497SAlan Somers 	case FUSE_RMDIR:
479bf507497SAlan Somers 	case FUSE_SYMLINK:
480bf507497SAlan Somers 	case FUSE_UNLINK:
481bf507497SAlan Somers 		ASSERT_GT(inlen, fih) << "Missing request filename";
482bf507497SAlan Somers 		break;
483bf507497SAlan Somers 	case FUSE_FORGET:
484bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.forget));
485bf507497SAlan Somers 		break;
486bf507497SAlan Somers 	case FUSE_GETATTR:
487bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.getattr));
488bf507497SAlan Somers 		break;
489bf507497SAlan Somers 	case FUSE_SETATTR:
490bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.setattr));
491bf507497SAlan Somers 		break;
492bf507497SAlan Somers 	case FUSE_READLINK:
493bf507497SAlan Somers 		ASSERT_EQ(inlen, fih) << "Unexpected request body";
494bf507497SAlan Somers 		break;
495bf507497SAlan Somers 	case FUSE_MKNOD:
496bf507497SAlan Somers 		{
497bf507497SAlan Somers 			size_t s;
498bf507497SAlan Somers 			if (m_kernel_minor_version >= 12)
499bf507497SAlan Somers 				s = sizeof(in.body.mknod);
500bf507497SAlan Somers 			else
501bf507497SAlan Somers 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
502bf507497SAlan Somers 			ASSERT_GE(inlen, fih + s) << "Missing request body";
503bf507497SAlan Somers 			ASSERT_GT(inlen, fih + s) << "Missing request filename";
504bf507497SAlan Somers 			break;
505bf507497SAlan Somers 		}
506bf507497SAlan Somers 	case FUSE_MKDIR:
507bf507497SAlan Somers 		ASSERT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
508bf507497SAlan Somers 			"Missing request body";
509bf507497SAlan Somers 		ASSERT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
510bf507497SAlan Somers 			"Missing request filename";
511bf507497SAlan Somers 		break;
512bf507497SAlan Somers 	case FUSE_RENAME:
513bf507497SAlan Somers 		ASSERT_GE(inlen, fih + sizeof(in.body.rename)) <<
514bf507497SAlan Somers 			"Missing request body";
515bf507497SAlan Somers 		ASSERT_GT(inlen, fih + sizeof(in.body.rename)) <<
516bf507497SAlan Somers 			"Missing request filename";
517bf507497SAlan Somers 		break;
518bf507497SAlan Somers 	case FUSE_LINK:
519bf507497SAlan Somers 		ASSERT_GE(inlen, fih + sizeof(in.body.link)) <<
520bf507497SAlan Somers 			"Missing request body";
521bf507497SAlan Somers 		ASSERT_GT(inlen, fih + sizeof(in.body.link)) <<
522bf507497SAlan Somers 			"Missing request filename";
523bf507497SAlan Somers 		break;
524bf507497SAlan Somers 	case FUSE_OPEN:
525bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.open));
526bf507497SAlan Somers 		break;
527bf507497SAlan Somers 	case FUSE_READ:
528bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.read));
529bf507497SAlan Somers 		break;
530bf507497SAlan Somers 	case FUSE_WRITE:
531bf507497SAlan Somers 		{
532bf507497SAlan Somers 			size_t s;
533bf507497SAlan Somers 
534bf507497SAlan Somers 			if (m_kernel_minor_version >= 9)
535bf507497SAlan Somers 				s = sizeof(in.body.write);
536bf507497SAlan Somers 			else
537bf507497SAlan Somers 				s = FUSE_COMPAT_WRITE_IN_SIZE;
538bf507497SAlan Somers 			ASSERT_GE(inlen, fih + s) << "Missing request body";
539bf507497SAlan Somers 			// I suppose a 0-byte write should be allowed
540bf507497SAlan Somers 			break;
541bf507497SAlan Somers 		}
542bf507497SAlan Somers 	case FUSE_DESTROY:
543bf507497SAlan Somers 	case FUSE_STATFS:
544bf507497SAlan Somers 		ASSERT_EQ(inlen, fih);
545bf507497SAlan Somers 		break;
546bf507497SAlan Somers 	case FUSE_RELEASE:
547bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.release));
548bf507497SAlan Somers 		break;
549bf507497SAlan Somers 	case FUSE_FSYNC:
550bf507497SAlan Somers 	case FUSE_FSYNCDIR:
551bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.fsync));
552bf507497SAlan Somers 		break;
553bf507497SAlan Somers 	case FUSE_SETXATTR:
554bf507497SAlan Somers 		ASSERT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
555bf507497SAlan Somers 			"Missing request body";
556bf507497SAlan Somers 		ASSERT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
557bf507497SAlan Somers 			"Missing request attribute name";
558bf507497SAlan Somers 		break;
559bf507497SAlan Somers 	case FUSE_GETXATTR:
5603a79e8e7SAlan Somers 		ASSERT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
561bf507497SAlan Somers 			"Missing request body";
5623a79e8e7SAlan Somers 		ASSERT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
563bf507497SAlan Somers 			"Missing request attribute name";
564bf507497SAlan Somers 		break;
565bf507497SAlan Somers 	case FUSE_LISTXATTR:
5663a79e8e7SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.listxattr));
567bf507497SAlan Somers 		break;
568bf507497SAlan Somers 	case FUSE_REMOVEXATTR:
569bf507497SAlan Somers 		ASSERT_GT(inlen, fih) << "Missing request attribute name";
570bf507497SAlan Somers 		break;
571bf507497SAlan Somers 	case FUSE_FLUSH:
572bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.flush));
573bf507497SAlan Somers 		break;
574bf507497SAlan Somers 	case FUSE_INIT:
575bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.init));
576bf507497SAlan Somers 		break;
577bf507497SAlan Somers 	case FUSE_OPENDIR:
578bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.opendir));
579bf507497SAlan Somers 		break;
580bf507497SAlan Somers 	case FUSE_READDIR:
581bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.readdir));
582bf507497SAlan Somers 		break;
583bf507497SAlan Somers 	case FUSE_RELEASEDIR:
584bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.releasedir));
585bf507497SAlan Somers 		break;
586bf507497SAlan Somers 	case FUSE_GETLK:
587bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.getlk));
588bf507497SAlan Somers 		break;
589bf507497SAlan Somers 	case FUSE_SETLK:
590bf507497SAlan Somers 	case FUSE_SETLKW:
591bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.setlk));
592bf507497SAlan Somers 		break;
593bf507497SAlan Somers 	case FUSE_ACCESS:
594bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.access));
595bf507497SAlan Somers 		break;
596bf507497SAlan Somers 	case FUSE_CREATE:
597bf507497SAlan Somers 		ASSERT_GE(inlen, fih + sizeof(in.body.create)) <<
598bf507497SAlan Somers 			"Missing request body";
599bf507497SAlan Somers 		ASSERT_GT(inlen, fih + sizeof(in.body.create)) <<
600bf507497SAlan Somers 			"Missing request filename";
601bf507497SAlan Somers 		break;
602bf507497SAlan Somers 	case FUSE_INTERRUPT:
603bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.interrupt));
604bf507497SAlan Somers 		break;
605bf507497SAlan Somers 	case FUSE_BMAP:
606bf507497SAlan Somers 		ASSERT_EQ(inlen, fih + sizeof(in.body.bmap));
607bf507497SAlan Somers 		break;
608bf507497SAlan Somers 	case FUSE_NOTIFY_REPLY:
609bf507497SAlan Somers 	case FUSE_BATCH_FORGET:
610bf507497SAlan Somers 	case FUSE_FALLOCATE:
611bf507497SAlan Somers 	case FUSE_IOCTL:
612bf507497SAlan Somers 	case FUSE_POLL:
613bf507497SAlan Somers 	case FUSE_READDIRPLUS:
614bf507497SAlan Somers 		FAIL() << "Unsupported opcode?";
615bf507497SAlan Somers 	default:
616bf507497SAlan Somers 		FAIL() << "Unknown opcode " << in.header.opcode;
617bf507497SAlan Somers 	}
618bf507497SAlan Somers }
619bf507497SAlan Somers 
6209821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
62129edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
62229edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
6239821f1d3SAlan Somers 
62429edc611SAlan Somers 	read_request(*in);
6259821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
6269821f1d3SAlan Somers 
6279821f1d3SAlan Somers 	out->header.unique = in->header.unique;
6289821f1d3SAlan Somers 	out->header.error = 0;
6299821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
63016bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
6319821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
6328eecd9ceSAlan Somers 	out->body.init.max_write = m_maxwrite;
6339821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
63487ff949aSAlan Somers 
63587ff949aSAlan Somers 	if (m_kernel_minor_version < 23) {
63687ff949aSAlan Somers 		SET_OUT_HEADER_LEN(*out, init_7_22);
63787ff949aSAlan Somers 	} else {
638fef46454SAlan Somers 		out->body.init.time_gran = m_time_gran;
63929edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out, init);
64087ff949aSAlan Somers 	}
64187ff949aSAlan Somers 
64229edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
6439821f1d3SAlan Somers }
6449821f1d3SAlan Somers 
6459821f1d3SAlan Somers void MockFS::kill_daemon() {
64681a619c4SAlan Somers 	m_quit = true;
6478b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
6489821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
6498b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
6508b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
6518b73a4c5SAlan Somers 	// during the unmount sequence.
6529821f1d3SAlan Somers 	close(m_fuse_fd);
6538b73a4c5SAlan Somers 	m_fuse_fd = -1;
6549821f1d3SAlan Somers }
6559821f1d3SAlan Somers 
6569821f1d3SAlan Somers void MockFS::loop() {
65729edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
6589821f1d3SAlan Somers 
65929edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
6609821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
66181a619c4SAlan Somers 	while (!m_quit) {
66229edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
66329edc611SAlan Somers 		read_request(*in);
66481a619c4SAlan Somers 		if (m_quit)
6659821f1d3SAlan Somers 			break;
6669821f1d3SAlan Somers 		if (verbosity > 0)
667c2d70d6eSAlan Somers 			debug_request(*in);
668bf507497SAlan Somers 		audit_request(*in);
6699821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
67029edc611SAlan Somers 			process(*in, out);
6719821f1d3SAlan Somers 		} else {
6729821f1d3SAlan Somers 			/*
6739821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
6749821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
6759821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
6769821f1d3SAlan Somers 			 */
67799cf7bffSAlan Somers 			if (verbosity > 1)
67899cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
67999cf7bffSAlan Somers 					in->header.pid);
68029edc611SAlan Somers 			process_default(*in, out);
6819821f1d3SAlan Somers 		}
68229edc611SAlan Somers 		for (auto &it: out)
68329edc611SAlan Somers 			write_response(*it);
6849821f1d3SAlan Somers 		out.clear();
6859821f1d3SAlan Somers 	}
6869821f1d3SAlan Somers }
6879821f1d3SAlan Somers 
688c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
689c2d70d6eSAlan Somers {
690c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
691c2d70d6eSAlan Somers 
692c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
693c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
694c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
695c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
696c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
697c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
698c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
699c2d70d6eSAlan Somers 		namelen;
700c2d70d6eSAlan Somers 	debug_response(*out);
701c2d70d6eSAlan Somers 	write_response(*out);
702c2d70d6eSAlan Somers 	return 0;
703c2d70d6eSAlan Somers }
704c2d70d6eSAlan Somers 
705eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
706eae1ae13SAlan Somers {
707eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
708eae1ae13SAlan Somers 
709eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
710eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
711eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
712eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
713eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
714eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
715eae1ae13SAlan Somers 	debug_response(*out);
716eae1ae13SAlan Somers 	write_response(*out);
717eae1ae13SAlan Somers 	return 0;
718eae1ae13SAlan Somers }
719eae1ae13SAlan Somers 
7205a0b9a27SAlan Somers int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
7217cbb8e8aSAlan Somers {
7227cbb8e8aSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
7237cbb8e8aSAlan Somers 
7247cbb8e8aSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
7257cbb8e8aSAlan Somers 	out->header.error = FUSE_NOTIFY_STORE;
7267cbb8e8aSAlan Somers 	out->body.store.nodeid = ino;
7277cbb8e8aSAlan Somers 	out->body.store.offset = off;
7287cbb8e8aSAlan Somers 	out->body.store.size = size;
7297cbb8e8aSAlan Somers 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
7307cbb8e8aSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
7317cbb8e8aSAlan Somers 	debug_response(*out);
7327cbb8e8aSAlan Somers 	write_response(*out);
7337cbb8e8aSAlan Somers 	return 0;
7347cbb8e8aSAlan Somers }
7357cbb8e8aSAlan Somers 
7369821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
7379821f1d3SAlan Somers 	if (pid == m_pid) {
7389821f1d3SAlan Somers 		return (true);
73991ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
74091ff3a0dSAlan Somers 		return (true);
7419821f1d3SAlan Somers 	} else {
7429821f1d3SAlan Somers 		struct kinfo_proc *ki;
7439821f1d3SAlan Somers 		bool ok = false;
7449821f1d3SAlan Somers 
7459821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
7469821f1d3SAlan Somers 		if (ki == NULL)
7479821f1d3SAlan Somers 			return (false);
7489821f1d3SAlan Somers 		/*
7499821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
7509821f1d3SAlan Somers 		 * can use aio functions
7519821f1d3SAlan Somers 		 */
7529821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
7539821f1d3SAlan Somers 			ok = true;
7549821f1d3SAlan Somers 		free(ki);
7559821f1d3SAlan Somers 		return (ok);
7569821f1d3SAlan Somers 	}
7579821f1d3SAlan Somers }
7589821f1d3SAlan Somers 
75929edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
76029edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
7619821f1d3SAlan Somers {
76229edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
76329edc611SAlan Somers 	out0->header.unique = in.header.unique;
7649821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
7659821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
76629edc611SAlan Somers 	out.push_back(std::move(out0));
7679821f1d3SAlan Somers }
7689821f1d3SAlan Somers 
76929edc611SAlan Somers void MockFS::read_request(mockfs_buf_in &in) {
7709821f1d3SAlan Somers 	ssize_t res;
77177fbe694SAlan Somers 	int nready = 0;
7723429092cSAlan Somers 	fd_set readfds;
7733429092cSAlan Somers 	pollfd fds[1];
7743429092cSAlan Somers 	struct kevent changes[1];
7753429092cSAlan Somers 	struct kevent events[1];
77677fbe694SAlan Somers 	struct timespec timeout_ts;
77777fbe694SAlan Somers 	struct timeval timeout_tv;
77877fbe694SAlan Somers 	const int timeout_ms = 999;
77977fbe694SAlan Somers 	int timeout_int, nfds;
7809821f1d3SAlan Somers 
7813429092cSAlan Somers 	switch (m_pm) {
7823429092cSAlan Somers 	case BLOCKING:
7833429092cSAlan Somers 		break;
7843429092cSAlan Somers 	case KQ:
78577fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
78677fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
78777fbe694SAlan Somers 		while (nready == 0) {
78877fbe694SAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
78977fbe694SAlan Somers 				0, 0);
79077fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
79177fbe694SAlan Somers 				&timeout_ts);
7923429092cSAlan Somers 			if (m_quit)
7933429092cSAlan Somers 				return;
79477fbe694SAlan Somers 		}
7953429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
7963429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
7973429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
7983429092cSAlan Somers 			FAIL() << strerror(events[0].data);
7993429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
8003429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
8010a7c63e0SAlan Somers 		m_nready = events[0].data;
8023429092cSAlan Somers 		break;
8033429092cSAlan Somers 	case POLL:
80477fbe694SAlan Somers 		timeout_int = timeout_ms;
8053429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
8063429092cSAlan Somers 		fds[0].events = POLLIN;
80777fbe694SAlan Somers 		while (nready == 0) {
80877fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
8093429092cSAlan Somers 			if (m_quit)
8103429092cSAlan Somers 				return;
81177fbe694SAlan Somers 		}
8123429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
8133429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
8143429092cSAlan Somers 		break;
8153429092cSAlan Somers 	case SELECT:
81677fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
81777fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
81877fbe694SAlan Somers 		nfds = m_fuse_fd + 1;
81977fbe694SAlan Somers 		while (nready == 0) {
8203429092cSAlan Somers 			FD_ZERO(&readfds);
8213429092cSAlan Somers 			FD_SET(m_fuse_fd, &readfds);
82277fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
82377fbe694SAlan Somers 				&timeout_tv);
8243429092cSAlan Somers 			if (m_quit)
8253429092cSAlan Somers 				return;
82677fbe694SAlan Somers 		}
8273429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
8283429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
8293429092cSAlan Somers 		break;
8303429092cSAlan Somers 	default:
8313429092cSAlan Somers 		FAIL() << "not yet implemented";
8323429092cSAlan Somers 	}
83329edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
8343429092cSAlan Somers 
835b690d120SAlan Somers 	if (res < 0 && !m_quit) {
836b690d120SAlan Somers 		m_quit = true;
837*8e765737SAlan Somers 		FAIL() << "read: " << strerror(errno);
838b690d120SAlan Somers 	}
83929edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
840bf507497SAlan Somers 	/*
841bf507497SAlan Somers 	 * Inconsistently, fuse_in_header.len is the size of the entire
842bf507497SAlan Somers 	 * request,including header, even though fuse_out_header.len excludes
843bf507497SAlan Somers 	 * the size of the header.
844bf507497SAlan Somers 	 */
84538a3e0bdSAlan Somers 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
8469821f1d3SAlan Somers }
8479821f1d3SAlan Somers 
84829edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
8493429092cSAlan Somers 	fd_set writefds;
8503429092cSAlan Somers 	pollfd fds[1];
8513429092cSAlan Somers 	int nready, nfds;
8523429092cSAlan Somers 	ssize_t r;
8533429092cSAlan Somers 
8543429092cSAlan Somers 	switch (m_pm) {
8553429092cSAlan Somers 	case BLOCKING:
8563429092cSAlan Somers 	case KQ:	/* EVFILT_WRITE is not supported */
8573429092cSAlan Somers 		break;
8583429092cSAlan Somers 	case POLL:
8593429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
8603429092cSAlan Somers 		fds[0].events = POLLOUT;
8613429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
8623429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
8633429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
8643429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
8653429092cSAlan Somers 		break;
8663429092cSAlan Somers 	case SELECT:
8673429092cSAlan Somers 		FD_ZERO(&writefds);
8683429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
8693429092cSAlan Somers 		nfds = m_fuse_fd + 1;
8703429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
8713429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
8723429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
8733429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
8743429092cSAlan Somers 		break;
8753429092cSAlan Somers 	default:
8763429092cSAlan Somers 		FAIL() << "not yet implemented";
8773429092cSAlan Somers 	}
87829edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
8793429092cSAlan Somers 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
8803429092cSAlan Somers }
8813429092cSAlan Somers 
8829821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
8839821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
8849821f1d3SAlan Somers 
8859821f1d3SAlan Somers 	mock_fs->loop();
8869821f1d3SAlan Somers 
8879821f1d3SAlan Somers 	return (NULL);
8889821f1d3SAlan Somers }
8899821f1d3SAlan Somers 
8909821f1d3SAlan Somers void MockFS::unmount() {
8919821f1d3SAlan Somers 	::unmount("mountpoint", 0);
8929821f1d3SAlan Somers }
893