xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 37df9d3bba8577fcdd63382ff5a4a5cbb4aa55b4)
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 {
65*37df9d3bSAlan Somers 	const char* table[] = {
669821f1d3SAlan Somers 		"Unknown (opcode 0)",
679821f1d3SAlan Somers 		"LOOKUP",
689821f1d3SAlan Somers 		"FORGET",
699821f1d3SAlan Somers 		"GETATTR",
709821f1d3SAlan Somers 		"SETATTR",
719821f1d3SAlan Somers 		"READLINK",
729821f1d3SAlan Somers 		"SYMLINK",
739821f1d3SAlan Somers 		"Unknown (opcode 7)",
749821f1d3SAlan Somers 		"MKNOD",
759821f1d3SAlan Somers 		"MKDIR",
769821f1d3SAlan Somers 		"UNLINK",
779821f1d3SAlan Somers 		"RMDIR",
789821f1d3SAlan Somers 		"RENAME",
799821f1d3SAlan Somers 		"LINK",
809821f1d3SAlan Somers 		"OPEN",
819821f1d3SAlan Somers 		"READ",
829821f1d3SAlan Somers 		"WRITE",
839821f1d3SAlan Somers 		"STATFS",
849821f1d3SAlan Somers 		"RELEASE",
859821f1d3SAlan Somers 		"Unknown (opcode 19)",
869821f1d3SAlan Somers 		"FSYNC",
879821f1d3SAlan Somers 		"SETXATTR",
889821f1d3SAlan Somers 		"GETXATTR",
899821f1d3SAlan Somers 		"LISTXATTR",
909821f1d3SAlan Somers 		"REMOVEXATTR",
919821f1d3SAlan Somers 		"FLUSH",
929821f1d3SAlan Somers 		"INIT",
939821f1d3SAlan Somers 		"OPENDIR",
949821f1d3SAlan Somers 		"READDIR",
959821f1d3SAlan Somers 		"RELEASEDIR",
969821f1d3SAlan Somers 		"FSYNCDIR",
979821f1d3SAlan Somers 		"GETLK",
989821f1d3SAlan Somers 		"SETLK",
999821f1d3SAlan Somers 		"SETLKW",
1009821f1d3SAlan Somers 		"ACCESS",
1019821f1d3SAlan Somers 		"CREATE",
1029821f1d3SAlan Somers 		"INTERRUPT",
1039821f1d3SAlan Somers 		"BMAP",
104*37df9d3bSAlan Somers 		"DESTROY",
105*37df9d3bSAlan Somers 		"IOCTL",
106*37df9d3bSAlan Somers 		"POLL",
107*37df9d3bSAlan Somers 		"NOTIFY_REPLY",
108*37df9d3bSAlan Somers 		"BATCH_FORGET",
109*37df9d3bSAlan Somers 		"FALLOCATE",
110*37df9d3bSAlan Somers 		"READDIRPLUS",
111*37df9d3bSAlan Somers 		"RENAME2",
112*37df9d3bSAlan Somers 		"LSEEK",
1139821f1d3SAlan Somers 	};
114*37df9d3bSAlan Somers 	if (opcode >= nitems(table))
1159821f1d3SAlan Somers 		return ("Unknown (opcode > max)");
1169821f1d3SAlan Somers 	else
1179821f1d3SAlan Somers 		return (table[opcode]);
1189821f1d3SAlan Somers }
1199821f1d3SAlan Somers 
1209821f1d3SAlan Somers ProcessMockerT
1219821f1d3SAlan Somers ReturnErrno(int error)
1229821f1d3SAlan Somers {
1239821f1d3SAlan Somers 	return([=](auto in, auto &out) {
12429edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
12529edc611SAlan Somers 		out0->header.unique = in.header.unique;
1269821f1d3SAlan Somers 		out0->header.error = -error;
1279821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
12829edc611SAlan Somers 		out.push_back(std::move(out0));
1299821f1d3SAlan Somers 	});
1309821f1d3SAlan Somers }
1319821f1d3SAlan Somers 
1329821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
1339821f1d3SAlan Somers ProcessMockerT
1349821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
1359821f1d3SAlan Somers {
1369821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1379821f1d3SAlan Somers 		/* nodeid means ENOENT and cache it */
13829edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
1399821f1d3SAlan Somers 		out0->body.entry.nodeid = 0;
14029edc611SAlan Somers 		out0->header.unique = in.header.unique;
1419821f1d3SAlan Somers 		out0->header.error = 0;
1429821f1d3SAlan Somers 		out0->body.entry.entry_valid = entry_valid->tv_sec;
1439821f1d3SAlan Somers 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
14429edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
14529edc611SAlan Somers 		out.push_back(std::move(out0));
1469821f1d3SAlan Somers 	});
1479821f1d3SAlan Somers }
1489821f1d3SAlan Somers 
1499821f1d3SAlan Somers ProcessMockerT
15029edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in,
15129edc611SAlan Somers 				   struct mockfs_buf_out &out)> f)
1529821f1d3SAlan Somers {
15329edc611SAlan Somers 	return([=](auto& in, auto &out) {
15429edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
15529edc611SAlan Somers 		out0->header.unique = in.header.unique;
15629edc611SAlan Somers 		f(in, *out0);
15729edc611SAlan Somers 		out.push_back(std::move(out0));
1589821f1d3SAlan Somers 	});
1599821f1d3SAlan Somers }
1609821f1d3SAlan Somers 
1619821f1d3SAlan Somers void sigint_handler(int __unused sig) {
1628b73a4c5SAlan Somers 	// Don't do anything except interrupt the daemon's read(2) call
1639821f1d3SAlan Somers }
1649821f1d3SAlan Somers 
1656c0c3620SAlan Somers void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
1669821f1d3SAlan Somers {
16729edc611SAlan Somers 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
16829edc611SAlan Somers 		in.header.nodeid);
1699821f1d3SAlan Somers 	if (verbosity > 1) {
1706c0c3620SAlan Somers 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
1716c0c3620SAlan Somers 			" buflen=%zd",
17229edc611SAlan Somers 			in.header.uid, in.header.gid, in.header.pid,
1736c0c3620SAlan Somers 			in.header.unique, in.header.len, buflen);
1749821f1d3SAlan Somers 	}
17529edc611SAlan Somers 	switch (in.header.opcode) {
17619ef317dSAlan Somers 		const char *name, *value;
17719ef317dSAlan Somers 
178caf5f57dSAlan Somers 		case FUSE_ACCESS:
17929edc611SAlan Somers 			printf(" mask=%#x", in.body.access.mask);
180caf5f57dSAlan Somers 			break;
181a1c9f4adSAlan Somers 		case FUSE_BMAP:
18297b0512bSAlan Somers 			printf(" block=%" PRIx64 " blocksize=%#x",
18397b0512bSAlan Somers 				in.body.bmap.block, in.body.bmap.blocksize);
184a1c9f4adSAlan Somers 			break;
18519ef317dSAlan Somers 		case FUSE_CREATE:
186a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
187a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
188a4856c96SAlan Somers 					sizeof(fuse_create_in);
189a4856c96SAlan Somers 			else
19029edc611SAlan Somers 				name = (const char*)in.body.bytes +
19119ef317dSAlan Somers 					sizeof(fuse_open_in);
19219ef317dSAlan Somers 			printf(" flags=%#x name=%s",
19329edc611SAlan Somers 				in.body.open.flags, name);
19419ef317dSAlan Somers 			break;
1959821f1d3SAlan Somers 		case FUSE_FLUSH:
196cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
19729edc611SAlan Somers 				in.body.flush.fh,
19829edc611SAlan Somers 				in.body.flush.lock_owner);
1999821f1d3SAlan Somers 			break;
2009821f1d3SAlan Somers 		case FUSE_FORGET:
20129edc611SAlan Somers 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
2029821f1d3SAlan Somers 			break;
2039821f1d3SAlan Somers 		case FUSE_FSYNC:
20429edc611SAlan Somers 			printf(" flags=%#x", in.body.fsync.fsync_flags);
2059821f1d3SAlan Somers 			break;
2069821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
20729edc611SAlan Somers 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
2089821f1d3SAlan Somers 			break;
209723c7768SAlan Somers 		case FUSE_INTERRUPT:
21029edc611SAlan Somers 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
211723c7768SAlan Somers 			break;
212002e54b0SAlan Somers 		case FUSE_LINK:
21329edc611SAlan Somers 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
214002e54b0SAlan Somers 			break;
2155e633330SAlan Somers 		case FUSE_LISTXATTR:
2165e633330SAlan Somers 			printf(" size=%" PRIu32, in.body.listxattr.size);
2175e633330SAlan Somers 			break;
2189821f1d3SAlan Somers 		case FUSE_LOOKUP:
21929edc611SAlan Somers 			printf(" %s", in.body.lookup);
2209821f1d3SAlan Somers 			break;
221*37df9d3bSAlan Somers 		case FUSE_LSEEK:
222*37df9d3bSAlan Somers 			switch (in.body.lseek.whence) {
223*37df9d3bSAlan Somers 			case SEEK_HOLE:
224*37df9d3bSAlan Somers 				printf(" SEEK_HOLE offset=%ld",
225*37df9d3bSAlan Somers 				    in.body.lseek.offset);
226*37df9d3bSAlan Somers 				break;
227*37df9d3bSAlan Somers 			case SEEK_DATA:
228*37df9d3bSAlan Somers 				printf(" SEEK_DATA offset=%ld",
229*37df9d3bSAlan Somers 				    in.body.lseek.offset);
230*37df9d3bSAlan Somers 				break;
231*37df9d3bSAlan Somers 			default:
232*37df9d3bSAlan Somers 				printf(" whence=%u offset=%ld",
233*37df9d3bSAlan Somers 				    in.body.lseek.whence, in.body.lseek.offset);
234*37df9d3bSAlan Somers 				break;
235*37df9d3bSAlan Somers 			}
236*37df9d3bSAlan Somers 			break;
23799cf7bffSAlan Somers 		case FUSE_MKDIR:
23829edc611SAlan Somers 			name = (const char*)in.body.bytes +
23999cf7bffSAlan Somers 				sizeof(fuse_mkdir_in);
240a4856c96SAlan Somers 			printf(" name=%s mode=%#o umask=%#o", name,
241a4856c96SAlan Somers 				in.body.mkdir.mode, in.body.mkdir.umask);
24299cf7bffSAlan Somers 			break;
243bf4d7084SAlan Somers 		case FUSE_MKNOD:
244a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
245a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
246a4856c96SAlan Somers 					sizeof(fuse_mknod_in);
247a4856c96SAlan Somers 			else
248a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
249a4856c96SAlan Somers 					FUSE_COMPAT_MKNOD_IN_SIZE;
250a4856c96SAlan Somers 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
251a4856c96SAlan Somers 				in.body.mknod.mode, in.body.mknod.rdev,
252a4856c96SAlan Somers 				in.body.mknod.umask, name);
253bf4d7084SAlan Somers 			break;
2549821f1d3SAlan Somers 		case FUSE_OPEN:
255a4856c96SAlan Somers 			printf(" flags=%#x", in.body.open.flags);
2569821f1d3SAlan Somers 			break;
2579821f1d3SAlan Somers 		case FUSE_OPENDIR:
258a4856c96SAlan Somers 			printf(" flags=%#x", in.body.opendir.flags);
2599821f1d3SAlan Somers 			break;
2609821f1d3SAlan Somers 		case FUSE_READ:
261cc04566cSAlan Somers 			printf(" offset=%" PRIu64 " size=%u",
26229edc611SAlan Somers 				in.body.read.offset,
26329edc611SAlan Somers 				in.body.read.size);
264d4fd0c81SAlan Somers 			if (verbosity > 1)
265d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.read.flags);
2669821f1d3SAlan Somers 			break;
2679821f1d3SAlan Somers 		case FUSE_READDIR:
268cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
26929edc611SAlan Somers 				in.body.readdir.fh, in.body.readdir.offset,
27029edc611SAlan Somers 				in.body.readdir.size);
2719821f1d3SAlan Somers 			break;
2729821f1d3SAlan Somers 		case FUSE_RELEASE:
273cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
27429edc611SAlan Somers 				in.body.release.fh,
27529edc611SAlan Somers 				in.body.release.flags,
27629edc611SAlan Somers 				in.body.release.lock_owner);
2779821f1d3SAlan Somers 			break;
2789821f1d3SAlan Somers 		case FUSE_SETATTR:
2799821f1d3SAlan Somers 			if (verbosity <= 1) {
28029edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
2819821f1d3SAlan Somers 				break;
2829821f1d3SAlan Somers 			}
28329edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
28429edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
28529edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
28629edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
28729edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
28829edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
28929edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
29029edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
29129edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
292cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
29329edc611SAlan Somers 					in.body.setattr.atime,
29429edc611SAlan Somers 					in.body.setattr.atimensec);
29529edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
296cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
29729edc611SAlan Somers 					in.body.setattr.mtime,
29829edc611SAlan Somers 					in.body.setattr.mtimensec);
29929edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
30029edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
3019821f1d3SAlan Somers 			break;
302f067b609SAlan Somers 		case FUSE_SETLK:
303cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
304cc04566cSAlan Somers 				" type=%u pid=%u",
30529edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
30629edc611SAlan Somers 				in.body.setlk.lk.type,
30729edc611SAlan Somers 				in.body.setlk.lk.pid);
308f067b609SAlan Somers 			if (verbosity >= 2) {
309cc04566cSAlan Somers 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
31029edc611SAlan Somers 					in.body.setlk.lk.start,
31129edc611SAlan Somers 					in.body.setlk.lk.end);
312f067b609SAlan Somers 			}
313f067b609SAlan Somers 			break;
3149821f1d3SAlan Somers 		case FUSE_SETXATTR:
3159821f1d3SAlan Somers 			/*
3169821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
3179821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
3189821f1d3SAlan Somers 			 */
31929edc611SAlan Somers 			name = (const char*)in.body.bytes +
3209821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
32119ef317dSAlan Somers 			value = name + strlen(name) + 1;
32219ef317dSAlan Somers 			printf(" %s=%s", name, value);
3239821f1d3SAlan Somers 			break;
3249821f1d3SAlan Somers 		case FUSE_WRITE:
325cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
326d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
32729edc611SAlan Somers 				in.body.write.fh,
32829edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
32929edc611SAlan Somers 				in.body.write.write_flags);
330d4fd0c81SAlan Somers 			if (verbosity > 1)
331d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
3329821f1d3SAlan Somers 			break;
3339821f1d3SAlan Somers 		default:
3349821f1d3SAlan Somers 			break;
3359821f1d3SAlan Somers 	}
3369821f1d3SAlan Somers 	printf("\n");
3379821f1d3SAlan Somers }
3389821f1d3SAlan Somers 
339c2d70d6eSAlan Somers /*
340c2d70d6eSAlan Somers  * Debug a FUSE response.
341c2d70d6eSAlan Somers  *
342c2d70d6eSAlan Somers  * This is mostly useful for asynchronous notifications, which don't correspond
343c2d70d6eSAlan Somers  * to any request
344c2d70d6eSAlan Somers  */
345c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
346c2d70d6eSAlan Somers 	const char *name;
347c2d70d6eSAlan Somers 
348c2d70d6eSAlan Somers 	if (verbosity == 0)
349c2d70d6eSAlan Somers 		return;
350c2d70d6eSAlan Somers 
351c2d70d6eSAlan Somers 	switch (out.header.error) {
352c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
353c2d70d6eSAlan Somers 			name = (const char*)out.body.bytes +
354c2d70d6eSAlan Somers 				sizeof(fuse_notify_inval_entry_out);
355c2d70d6eSAlan Somers 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
356c2d70d6eSAlan Somers 				out.body.inval_entry.parent, name);
357c2d70d6eSAlan Somers 			break;
358eae1ae13SAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
359eae1ae13SAlan Somers 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
360eae1ae13SAlan Somers 				" len=%" PRIi64 "\n",
361eae1ae13SAlan Somers 				out.body.inval_inode.ino,
362eae1ae13SAlan Somers 				out.body.inval_inode.off,
363eae1ae13SAlan Somers 				out.body.inval_inode.len);
364eae1ae13SAlan Somers 			break;
3657cbb8e8aSAlan Somers 		case FUSE_NOTIFY_STORE:
3667cbb8e8aSAlan Somers 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
3677cbb8e8aSAlan Somers 				" size=%" PRIu32 "\n",
3687cbb8e8aSAlan Somers 				out.body.store.nodeid,
3697cbb8e8aSAlan Somers 				out.body.store.offset,
3707cbb8e8aSAlan Somers 				out.body.store.size);
3717cbb8e8aSAlan Somers 			break;
372c2d70d6eSAlan Somers 		default:
373c2d70d6eSAlan Somers 			break;
374c2d70d6eSAlan Somers 	}
375c2d70d6eSAlan Somers }
376c2d70d6eSAlan Somers 
37791ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
37816bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
379402b609cSAlan Somers 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
380ed74f781SAlan Somers 	bool noclusterr, unsigned time_gran, bool nointr)
3819821f1d3SAlan Somers {
3828b73a4c5SAlan Somers 	struct sigaction sa;
3839821f1d3SAlan Somers 	struct iovec *iov = NULL;
3849821f1d3SAlan Somers 	int iovlen = 0;
3859821f1d3SAlan Somers 	char fdstr[15];
38691ff3a0dSAlan Somers 	const bool trueval = true;
3879821f1d3SAlan Somers 
3889821f1d3SAlan Somers 	m_daemon_id = NULL;
38916bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
3909821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
391f928dbcbSAlan Somers 	m_maxwrite = MIN(max_write, max_max_write);
3920a7c63e0SAlan Somers 	m_nready = -1;
3933429092cSAlan Somers 	m_pm = pm;
394fef46454SAlan Somers 	m_time_gran = time_gran;
39581a619c4SAlan Somers 	m_quit = false;
3963429092cSAlan Somers 	if (m_pm == KQ)
3973429092cSAlan Somers 		m_kq = kqueue();
3983429092cSAlan Somers 	else
3993429092cSAlan Somers 		m_kq = -1;
4009821f1d3SAlan Somers 
4019821f1d3SAlan Somers 	/*
4029821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
4039821f1d3SAlan Somers 	 * mkdtemp
4049821f1d3SAlan Somers 	 */
4059821f1d3SAlan Somers 	/*
4069821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
4079821f1d3SAlan Somers 	 * instead.
4089821f1d3SAlan Somers 	 */
40991ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
4109821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4119821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
4129821f1d3SAlan Somers 
4133429092cSAlan Somers 	switch (m_pm) {
4143429092cSAlan Somers 	case BLOCKING:
41591ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
4163429092cSAlan Somers 		break;
4173429092cSAlan Somers 	default:
4183429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
4193429092cSAlan Somers 		break;
4203429092cSAlan Somers 	}
4219821f1d3SAlan Somers 	if (m_fuse_fd < 0)
4229821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4239821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
4249821f1d3SAlan Somers 
4259821f1d3SAlan Somers 	m_pid = getpid();
42691ff3a0dSAlan Somers 	m_child_pid = -1;
4279821f1d3SAlan Somers 
4289821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4299821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
4309821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
4319821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4323429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
4339821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
43491ff3a0dSAlan Somers 	if (allow_other) {
43591ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
4369821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4379821f1d3SAlan Somers 	}
4389821f1d3SAlan Somers 	if (default_permissions) {
4399821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
4409821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4419821f1d3SAlan Somers 	}
44291ff3a0dSAlan Somers 	if (push_symlinks_in) {
44391ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
44491ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
44591ff3a0dSAlan Somers 	}
446140bb492SAlan Somers 	if (ro) {
447140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
448140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
449140bb492SAlan Somers 	}
4508eecd9ceSAlan Somers 	if (async) {
4518eecd9ceSAlan Somers 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4528eecd9ceSAlan Somers 			sizeof(bool));
4538eecd9ceSAlan Somers 	}
454402b609cSAlan Somers 	if (noclusterr) {
455402b609cSAlan Somers 		build_iovec(&iov, &iovlen, "noclusterr",
456402b609cSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
457402b609cSAlan Somers 	}
458ed74f781SAlan Somers 	if (nointr) {
459ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "nointr",
460ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
461ed74f781SAlan Somers 	} else {
462ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "intr",
463ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
464ed74f781SAlan Somers 	}
4659821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
4669821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4679821f1d3SAlan Somers 			"Couldn't mount filesystem"));
4689821f1d3SAlan Somers 
4699821f1d3SAlan Somers 	// Setup default handler
4709821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
4719821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
4729821f1d3SAlan Somers 
4739821f1d3SAlan Somers 	init(flags);
4748b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
4758b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
4768b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
4778b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
4788b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4798b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
4809821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
4819821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4829821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
4839821f1d3SAlan Somers }
4849821f1d3SAlan Somers 
4859821f1d3SAlan Somers MockFS::~MockFS() {
4869821f1d3SAlan Somers 	kill_daemon();
4879821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
4889821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
4899821f1d3SAlan Somers 		m_daemon_id = NULL;
4909821f1d3SAlan Somers 	}
4918b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
4929821f1d3SAlan Somers 	rmdir("mountpoint");
4933429092cSAlan Somers 	if (m_kq >= 0)
4943429092cSAlan Somers 		close(m_kq);
4959821f1d3SAlan Somers }
4969821f1d3SAlan Somers 
4976c0c3620SAlan Somers void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
498bf507497SAlan Somers 	uint32_t inlen = in.header.len;
499bf507497SAlan Somers 	size_t fih = sizeof(in.header);
500bf507497SAlan Somers 	switch (in.header.opcode) {
501bf507497SAlan Somers 	case FUSE_LOOKUP:
502bf507497SAlan Somers 	case FUSE_RMDIR:
503bf507497SAlan Somers 	case FUSE_SYMLINK:
504bf507497SAlan Somers 	case FUSE_UNLINK:
5056c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request filename";
5066c0c3620SAlan Somers 		// No redundant information for checking buflen
507bf507497SAlan Somers 		break;
508bf507497SAlan Somers 	case FUSE_FORGET:
5096c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
5106c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
511bf507497SAlan Somers 		break;
512bf507497SAlan Somers 	case FUSE_GETATTR:
5136c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
5146c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
515bf507497SAlan Somers 		break;
516bf507497SAlan Somers 	case FUSE_SETATTR:
5176c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
5186c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
519bf507497SAlan Somers 		break;
520bf507497SAlan Somers 	case FUSE_READLINK:
5216c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
5226c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
523bf507497SAlan Somers 		break;
524bf507497SAlan Somers 	case FUSE_MKNOD:
525bf507497SAlan Somers 		{
526bf507497SAlan Somers 			size_t s;
527bf507497SAlan Somers 			if (m_kernel_minor_version >= 12)
528bf507497SAlan Somers 				s = sizeof(in.body.mknod);
529bf507497SAlan Somers 			else
530bf507497SAlan Somers 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
5316c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
5326c0c3620SAlan Somers 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
5336c0c3620SAlan Somers 			// No redundant information for checking buflen
534bf507497SAlan Somers 			break;
535bf507497SAlan Somers 		}
536bf507497SAlan Somers 	case FUSE_MKDIR:
5376c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
538bf507497SAlan Somers 			"Missing request body";
5396c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
540bf507497SAlan Somers 			"Missing request filename";
5416c0c3620SAlan Somers 		// No redundant information for checking buflen
542bf507497SAlan Somers 		break;
543bf507497SAlan Somers 	case FUSE_RENAME:
5446c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
545bf507497SAlan Somers 			"Missing request body";
5466c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
547bf507497SAlan Somers 			"Missing request filename";
5486c0c3620SAlan Somers 		// No redundant information for checking buflen
549bf507497SAlan Somers 		break;
550bf507497SAlan Somers 	case FUSE_LINK:
5516c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
552bf507497SAlan Somers 			"Missing request body";
5536c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
554bf507497SAlan Somers 			"Missing request filename";
5556c0c3620SAlan Somers 		// No redundant information for checking buflen
556bf507497SAlan Somers 		break;
557bf507497SAlan Somers 	case FUSE_OPEN:
5586c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
5596c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
560bf507497SAlan Somers 		break;
561bf507497SAlan Somers 	case FUSE_READ:
5626c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
5636c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
564bf507497SAlan Somers 		break;
565bf507497SAlan Somers 	case FUSE_WRITE:
566bf507497SAlan Somers 		{
567bf507497SAlan Somers 			size_t s;
568bf507497SAlan Somers 
569bf507497SAlan Somers 			if (m_kernel_minor_version >= 9)
570bf507497SAlan Somers 				s = sizeof(in.body.write);
571bf507497SAlan Somers 			else
572bf507497SAlan Somers 				s = FUSE_COMPAT_WRITE_IN_SIZE;
573bf507497SAlan Somers 			// I suppose a 0-byte write should be allowed
5746c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
5756c0c3620SAlan Somers 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
576bf507497SAlan Somers 			break;
577bf507497SAlan Somers 		}
578bf507497SAlan Somers 	case FUSE_DESTROY:
579bf507497SAlan Somers 	case FUSE_STATFS:
5806c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih);
5816c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
582bf507497SAlan Somers 		break;
583bf507497SAlan Somers 	case FUSE_RELEASE:
5846c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
5856c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
586bf507497SAlan Somers 		break;
587bf507497SAlan Somers 	case FUSE_FSYNC:
588bf507497SAlan Somers 	case FUSE_FSYNCDIR:
5896c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
5906c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
591bf507497SAlan Somers 		break;
592bf507497SAlan Somers 	case FUSE_SETXATTR:
5936c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
594bf507497SAlan Somers 			"Missing request body";
5956c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
596bf507497SAlan Somers 			"Missing request attribute name";
5976c0c3620SAlan Somers 		// No redundant information for checking buflen
598bf507497SAlan Somers 		break;
599bf507497SAlan Somers 	case FUSE_GETXATTR:
6006c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
601bf507497SAlan Somers 			"Missing request body";
6026c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
603bf507497SAlan Somers 			"Missing request attribute name";
6046c0c3620SAlan Somers 		// No redundant information for checking buflen
605bf507497SAlan Somers 		break;
606bf507497SAlan Somers 	case FUSE_LISTXATTR:
6076c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
6086c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
609bf507497SAlan Somers 		break;
610bf507497SAlan Somers 	case FUSE_REMOVEXATTR:
6116c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
6126c0c3620SAlan Somers 		// No redundant information for checking buflen
613bf507497SAlan Somers 		break;
614bf507497SAlan Somers 	case FUSE_FLUSH:
6156c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
6166c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
617bf507497SAlan Somers 		break;
618bf507497SAlan Somers 	case FUSE_INIT:
6196c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
6206c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
621bf507497SAlan Somers 		break;
622bf507497SAlan Somers 	case FUSE_OPENDIR:
6236c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
6246c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
625bf507497SAlan Somers 		break;
626bf507497SAlan Somers 	case FUSE_READDIR:
6276c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
6286c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
629bf507497SAlan Somers 		break;
630bf507497SAlan Somers 	case FUSE_RELEASEDIR:
6316c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
6326c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
633bf507497SAlan Somers 		break;
634bf507497SAlan Somers 	case FUSE_GETLK:
6356c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
6366c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
637bf507497SAlan Somers 		break;
638bf507497SAlan Somers 	case FUSE_SETLK:
639bf507497SAlan Somers 	case FUSE_SETLKW:
6406c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
6416c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
642bf507497SAlan Somers 		break;
643bf507497SAlan Somers 	case FUSE_ACCESS:
6446c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
6456c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
646bf507497SAlan Somers 		break;
647bf507497SAlan Somers 	case FUSE_CREATE:
6486c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
649bf507497SAlan Somers 			"Missing request body";
6506c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
651bf507497SAlan Somers 			"Missing request filename";
6526c0c3620SAlan Somers 		// No redundant information for checking buflen
653bf507497SAlan Somers 		break;
654bf507497SAlan Somers 	case FUSE_INTERRUPT:
6556c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
6566c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
657bf507497SAlan Somers 		break;
658bf507497SAlan Somers 	case FUSE_BMAP:
6596c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
6606c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
661bf507497SAlan Somers 		break;
662*37df9d3bSAlan Somers 	case FUSE_LSEEK:
663*37df9d3bSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
664*37df9d3bSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
665*37df9d3bSAlan Somers 		break;
666bf507497SAlan Somers 	case FUSE_NOTIFY_REPLY:
667bf507497SAlan Somers 	case FUSE_BATCH_FORGET:
668bf507497SAlan Somers 	case FUSE_FALLOCATE:
669bf507497SAlan Somers 	case FUSE_IOCTL:
670bf507497SAlan Somers 	case FUSE_POLL:
671bf507497SAlan Somers 	case FUSE_READDIRPLUS:
672bf507497SAlan Somers 		FAIL() << "Unsupported opcode?";
673bf507497SAlan Somers 	default:
674bf507497SAlan Somers 		FAIL() << "Unknown opcode " << in.header.opcode;
675bf507497SAlan Somers 	}
676bf507497SAlan Somers }
677bf507497SAlan Somers 
6789821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
6796c0c3620SAlan Somers 	ssize_t buflen;
6806c0c3620SAlan Somers 
68129edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
68229edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
6839821f1d3SAlan Somers 
6846c0c3620SAlan Somers 	read_request(*in, buflen);
6856c0c3620SAlan Somers 	audit_request(*in, buflen);
6869821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
6879821f1d3SAlan Somers 
6889821f1d3SAlan Somers 	out->header.unique = in->header.unique;
6899821f1d3SAlan Somers 	out->header.error = 0;
6909821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
69116bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
6929821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
6938eecd9ceSAlan Somers 	out->body.init.max_write = m_maxwrite;
6949821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
69587ff949aSAlan Somers 
69687ff949aSAlan Somers 	if (m_kernel_minor_version < 23) {
69787ff949aSAlan Somers 		SET_OUT_HEADER_LEN(*out, init_7_22);
69887ff949aSAlan Somers 	} else {
699fef46454SAlan Somers 		out->body.init.time_gran = m_time_gran;
70029edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out, init);
70187ff949aSAlan Somers 	}
70287ff949aSAlan Somers 
70329edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
7049821f1d3SAlan Somers }
7059821f1d3SAlan Somers 
7069821f1d3SAlan Somers void MockFS::kill_daemon() {
70781a619c4SAlan Somers 	m_quit = true;
7088b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
7099821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
7108b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
7118b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
7128b73a4c5SAlan Somers 	// during the unmount sequence.
7139821f1d3SAlan Somers 	close(m_fuse_fd);
7148b73a4c5SAlan Somers 	m_fuse_fd = -1;
7159821f1d3SAlan Somers }
7169821f1d3SAlan Somers 
7179821f1d3SAlan Somers void MockFS::loop() {
71829edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
7199821f1d3SAlan Somers 
72029edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
7219821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
72281a619c4SAlan Somers 	while (!m_quit) {
7236c0c3620SAlan Somers 		ssize_t buflen;
7246c0c3620SAlan Somers 
72529edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
7266c0c3620SAlan Somers 		read_request(*in, buflen);
72781a619c4SAlan Somers 		if (m_quit)
7289821f1d3SAlan Somers 			break;
7299821f1d3SAlan Somers 		if (verbosity > 0)
7306c0c3620SAlan Somers 			debug_request(*in, buflen);
7316c0c3620SAlan Somers 		audit_request(*in, buflen);
7329821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
73329edc611SAlan Somers 			process(*in, out);
7349821f1d3SAlan Somers 		} else {
7359821f1d3SAlan Somers 			/*
7369821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
7379821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
7389821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
7399821f1d3SAlan Somers 			 */
74099cf7bffSAlan Somers 			if (verbosity > 1)
74199cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
74299cf7bffSAlan Somers 					in->header.pid);
74329edc611SAlan Somers 			process_default(*in, out);
7449821f1d3SAlan Somers 		}
74529edc611SAlan Somers 		for (auto &it: out)
74629edc611SAlan Somers 			write_response(*it);
7479821f1d3SAlan Somers 		out.clear();
7489821f1d3SAlan Somers 	}
7499821f1d3SAlan Somers }
7509821f1d3SAlan Somers 
751c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
752c2d70d6eSAlan Somers {
753c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
754c2d70d6eSAlan Somers 
755c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
756c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
757c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
758c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
759c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
760c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
761c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
762c2d70d6eSAlan Somers 		namelen;
763c2d70d6eSAlan Somers 	debug_response(*out);
764c2d70d6eSAlan Somers 	write_response(*out);
765c2d70d6eSAlan Somers 	return 0;
766c2d70d6eSAlan Somers }
767c2d70d6eSAlan Somers 
768eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
769eae1ae13SAlan Somers {
770eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
771eae1ae13SAlan Somers 
772eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
773eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
774eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
775eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
776eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
777eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
778eae1ae13SAlan Somers 	debug_response(*out);
779eae1ae13SAlan Somers 	write_response(*out);
780eae1ae13SAlan Somers 	return 0;
781eae1ae13SAlan Somers }
782eae1ae13SAlan Somers 
7835a0b9a27SAlan Somers int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
7847cbb8e8aSAlan Somers {
7857cbb8e8aSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
7867cbb8e8aSAlan Somers 
7877cbb8e8aSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
7887cbb8e8aSAlan Somers 	out->header.error = FUSE_NOTIFY_STORE;
7897cbb8e8aSAlan Somers 	out->body.store.nodeid = ino;
7907cbb8e8aSAlan Somers 	out->body.store.offset = off;
7917cbb8e8aSAlan Somers 	out->body.store.size = size;
7927cbb8e8aSAlan Somers 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
7937cbb8e8aSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
7947cbb8e8aSAlan Somers 	debug_response(*out);
7957cbb8e8aSAlan Somers 	write_response(*out);
7967cbb8e8aSAlan Somers 	return 0;
7977cbb8e8aSAlan Somers }
7987cbb8e8aSAlan Somers 
7999821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
8009821f1d3SAlan Somers 	if (pid == m_pid) {
8019821f1d3SAlan Somers 		return (true);
80291ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
80391ff3a0dSAlan Somers 		return (true);
8049821f1d3SAlan Somers 	} else {
8059821f1d3SAlan Somers 		struct kinfo_proc *ki;
8069821f1d3SAlan Somers 		bool ok = false;
8079821f1d3SAlan Somers 
8089821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
8099821f1d3SAlan Somers 		if (ki == NULL)
8109821f1d3SAlan Somers 			return (false);
8119821f1d3SAlan Somers 		/*
8129821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
8139821f1d3SAlan Somers 		 * can use aio functions
8149821f1d3SAlan Somers 		 */
8159821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
8169821f1d3SAlan Somers 			ok = true;
8179821f1d3SAlan Somers 		free(ki);
8189821f1d3SAlan Somers 		return (ok);
8199821f1d3SAlan Somers 	}
8209821f1d3SAlan Somers }
8219821f1d3SAlan Somers 
82229edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
82329edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
8249821f1d3SAlan Somers {
82529edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
82629edc611SAlan Somers 	out0->header.unique = in.header.unique;
8279821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
8289821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
82929edc611SAlan Somers 	out.push_back(std::move(out0));
8309821f1d3SAlan Somers }
8319821f1d3SAlan Somers 
8326c0c3620SAlan Somers void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
83377fbe694SAlan Somers 	int nready = 0;
8343429092cSAlan Somers 	fd_set readfds;
8353429092cSAlan Somers 	pollfd fds[1];
8363429092cSAlan Somers 	struct kevent changes[1];
8373429092cSAlan Somers 	struct kevent events[1];
83877fbe694SAlan Somers 	struct timespec timeout_ts;
83977fbe694SAlan Somers 	struct timeval timeout_tv;
84077fbe694SAlan Somers 	const int timeout_ms = 999;
84177fbe694SAlan Somers 	int timeout_int, nfds;
8429821f1d3SAlan Somers 
8433429092cSAlan Somers 	switch (m_pm) {
8443429092cSAlan Somers 	case BLOCKING:
8453429092cSAlan Somers 		break;
8463429092cSAlan Somers 	case KQ:
84777fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
84877fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
84977fbe694SAlan Somers 		while (nready == 0) {
85077fbe694SAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
85177fbe694SAlan Somers 				0, 0);
85277fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
85377fbe694SAlan Somers 				&timeout_ts);
8543429092cSAlan Somers 			if (m_quit)
8553429092cSAlan Somers 				return;
85677fbe694SAlan Somers 		}
8573429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
8583429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
8593429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
8603429092cSAlan Somers 			FAIL() << strerror(events[0].data);
8613429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
8623429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
8630a7c63e0SAlan Somers 		m_nready = events[0].data;
8643429092cSAlan Somers 		break;
8653429092cSAlan Somers 	case POLL:
86677fbe694SAlan Somers 		timeout_int = timeout_ms;
8673429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
8683429092cSAlan Somers 		fds[0].events = POLLIN;
86977fbe694SAlan Somers 		while (nready == 0) {
87077fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
8713429092cSAlan Somers 			if (m_quit)
8723429092cSAlan Somers 				return;
87377fbe694SAlan Somers 		}
8743429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
8753429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
8763429092cSAlan Somers 		break;
8773429092cSAlan Somers 	case SELECT:
87877fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
87977fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
88077fbe694SAlan Somers 		nfds = m_fuse_fd + 1;
88177fbe694SAlan Somers 		while (nready == 0) {
8823429092cSAlan Somers 			FD_ZERO(&readfds);
8833429092cSAlan Somers 			FD_SET(m_fuse_fd, &readfds);
88477fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
88577fbe694SAlan Somers 				&timeout_tv);
8863429092cSAlan Somers 			if (m_quit)
8873429092cSAlan Somers 				return;
88877fbe694SAlan Somers 		}
8893429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
8903429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
8913429092cSAlan Somers 		break;
8923429092cSAlan Somers 	default:
8933429092cSAlan Somers 		FAIL() << "not yet implemented";
8943429092cSAlan Somers 	}
89529edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
8963429092cSAlan Somers 
897b690d120SAlan Somers 	if (res < 0 && !m_quit) {
898b690d120SAlan Somers 		m_quit = true;
8998e765737SAlan Somers 		FAIL() << "read: " << strerror(errno);
900b690d120SAlan Somers 	}
90129edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
902bf507497SAlan Somers 	/*
903bf507497SAlan Somers 	 * Inconsistently, fuse_in_header.len is the size of the entire
904bf507497SAlan Somers 	 * request,including header, even though fuse_out_header.len excludes
905bf507497SAlan Somers 	 * the size of the header.
906bf507497SAlan Somers 	 */
90738a3e0bdSAlan Somers 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
9089821f1d3SAlan Somers }
9099821f1d3SAlan Somers 
91029edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
9113429092cSAlan Somers 	fd_set writefds;
9123429092cSAlan Somers 	pollfd fds[1];
9133429092cSAlan Somers 	int nready, nfds;
9143429092cSAlan Somers 	ssize_t r;
9153429092cSAlan Somers 
9163429092cSAlan Somers 	switch (m_pm) {
9173429092cSAlan Somers 	case BLOCKING:
9183429092cSAlan Somers 	case KQ:	/* EVFILT_WRITE is not supported */
9193429092cSAlan Somers 		break;
9203429092cSAlan Somers 	case POLL:
9213429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
9223429092cSAlan Somers 		fds[0].events = POLLOUT;
9233429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
9243429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9253429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
9263429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
9273429092cSAlan Somers 		break;
9283429092cSAlan Somers 	case SELECT:
9293429092cSAlan Somers 		FD_ZERO(&writefds);
9303429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
9313429092cSAlan Somers 		nfds = m_fuse_fd + 1;
9323429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
9333429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9343429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
9353429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
9363429092cSAlan Somers 		break;
9373429092cSAlan Somers 	default:
9383429092cSAlan Somers 		FAIL() << "not yet implemented";
9393429092cSAlan Somers 	}
94029edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
9413429092cSAlan Somers 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
9423429092cSAlan Somers }
9433429092cSAlan Somers 
9449821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
9459821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
9469821f1d3SAlan Somers 
9479821f1d3SAlan Somers 	mock_fs->loop();
9489821f1d3SAlan Somers 
9499821f1d3SAlan Somers 	return (NULL);
9509821f1d3SAlan Somers }
9519821f1d3SAlan Somers 
9529821f1d3SAlan Somers void MockFS::unmount() {
9539821f1d3SAlan Somers 	::unmount("mountpoint", 0);
9549821f1d3SAlan Somers }
955