xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 97b0512b23f039252cf6f622486f07858cd4ea1f)
19821f1d3SAlan Somers /*-
29821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
39821f1d3SAlan Somers  *
49821f1d3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers  *
69821f1d3SAlan Somers  * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers  * from the FreeBSD Foundation.
89821f1d3SAlan Somers  *
99821f1d3SAlan Somers  * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers  * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers  * are met:
129821f1d3SAlan Somers  * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers  *    documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers  *
189821f1d3SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers  * SUCH DAMAGE.
299821f1d3SAlan Somers  */
309821f1d3SAlan Somers 
319821f1d3SAlan Somers extern "C" {
329821f1d3SAlan Somers #include <sys/param.h>
339821f1d3SAlan Somers 
349821f1d3SAlan Somers #include <sys/mount.h>
353429092cSAlan Somers #include <sys/select.h>
369821f1d3SAlan Somers #include <sys/stat.h>
379821f1d3SAlan Somers #include <sys/uio.h>
389821f1d3SAlan Somers #include <sys/user.h>
399821f1d3SAlan Somers 
409821f1d3SAlan Somers #include <fcntl.h>
419821f1d3SAlan Somers #include <libutil.h>
423429092cSAlan Somers #include <poll.h>
439821f1d3SAlan Somers #include <pthread.h>
449821f1d3SAlan Somers #include <signal.h>
459821f1d3SAlan Somers #include <stdlib.h>
469821f1d3SAlan Somers #include <unistd.h>
479821f1d3SAlan Somers 
489821f1d3SAlan Somers #include "mntopts.h"	// for build_iovec
499821f1d3SAlan Somers }
509821f1d3SAlan Somers 
51cc04566cSAlan Somers #include <cinttypes>
52cc04566cSAlan Somers 
539821f1d3SAlan Somers #include <gtest/gtest.h>
549821f1d3SAlan Somers 
559821f1d3SAlan Somers #include "mockfs.hh"
569821f1d3SAlan Somers 
579821f1d3SAlan Somers using namespace testing;
589821f1d3SAlan Somers 
599821f1d3SAlan Somers int verbosity = 0;
609821f1d3SAlan Somers 
619821f1d3SAlan Somers const char* opcode2opname(uint32_t opcode)
629821f1d3SAlan Somers {
639821f1d3SAlan Somers 	const int NUM_OPS = 39;
649821f1d3SAlan Somers 	const char* table[NUM_OPS] = {
659821f1d3SAlan Somers 		"Unknown (opcode 0)",
669821f1d3SAlan Somers 		"LOOKUP",
679821f1d3SAlan Somers 		"FORGET",
689821f1d3SAlan Somers 		"GETATTR",
699821f1d3SAlan Somers 		"SETATTR",
709821f1d3SAlan Somers 		"READLINK",
719821f1d3SAlan Somers 		"SYMLINK",
729821f1d3SAlan Somers 		"Unknown (opcode 7)",
739821f1d3SAlan Somers 		"MKNOD",
749821f1d3SAlan Somers 		"MKDIR",
759821f1d3SAlan Somers 		"UNLINK",
769821f1d3SAlan Somers 		"RMDIR",
779821f1d3SAlan Somers 		"RENAME",
789821f1d3SAlan Somers 		"LINK",
799821f1d3SAlan Somers 		"OPEN",
809821f1d3SAlan Somers 		"READ",
819821f1d3SAlan Somers 		"WRITE",
829821f1d3SAlan Somers 		"STATFS",
839821f1d3SAlan Somers 		"RELEASE",
849821f1d3SAlan Somers 		"Unknown (opcode 19)",
859821f1d3SAlan Somers 		"FSYNC",
869821f1d3SAlan Somers 		"SETXATTR",
879821f1d3SAlan Somers 		"GETXATTR",
889821f1d3SAlan Somers 		"LISTXATTR",
899821f1d3SAlan Somers 		"REMOVEXATTR",
909821f1d3SAlan Somers 		"FLUSH",
919821f1d3SAlan Somers 		"INIT",
929821f1d3SAlan Somers 		"OPENDIR",
939821f1d3SAlan Somers 		"READDIR",
949821f1d3SAlan Somers 		"RELEASEDIR",
959821f1d3SAlan Somers 		"FSYNCDIR",
969821f1d3SAlan Somers 		"GETLK",
979821f1d3SAlan Somers 		"SETLK",
989821f1d3SAlan Somers 		"SETLKW",
999821f1d3SAlan Somers 		"ACCESS",
1009821f1d3SAlan Somers 		"CREATE",
1019821f1d3SAlan Somers 		"INTERRUPT",
1029821f1d3SAlan Somers 		"BMAP",
1039821f1d3SAlan Somers 		"DESTROY"
1049821f1d3SAlan Somers 	};
1059821f1d3SAlan Somers 	if (opcode >= NUM_OPS)
1069821f1d3SAlan Somers 		return ("Unknown (opcode > max)");
1079821f1d3SAlan Somers 	else
1089821f1d3SAlan Somers 		return (table[opcode]);
1099821f1d3SAlan Somers }
1109821f1d3SAlan Somers 
1119821f1d3SAlan Somers ProcessMockerT
1129821f1d3SAlan Somers ReturnErrno(int error)
1139821f1d3SAlan Somers {
1149821f1d3SAlan Somers 	return([=](auto in, auto &out) {
11529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
11629edc611SAlan Somers 		out0->header.unique = in.header.unique;
1179821f1d3SAlan Somers 		out0->header.error = -error;
1189821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
11929edc611SAlan Somers 		out.push_back(std::move(out0));
1209821f1d3SAlan Somers 	});
1219821f1d3SAlan Somers }
1229821f1d3SAlan Somers 
1239821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
1249821f1d3SAlan Somers ProcessMockerT
1259821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
1269821f1d3SAlan Somers {
1279821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1289821f1d3SAlan Somers 		/* nodeid means ENOENT and cache it */
12929edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
1309821f1d3SAlan Somers 		out0->body.entry.nodeid = 0;
13129edc611SAlan Somers 		out0->header.unique = in.header.unique;
1329821f1d3SAlan Somers 		out0->header.error = 0;
1339821f1d3SAlan Somers 		out0->body.entry.entry_valid = entry_valid->tv_sec;
1349821f1d3SAlan Somers 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
13529edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
13629edc611SAlan Somers 		out.push_back(std::move(out0));
1379821f1d3SAlan Somers 	});
1389821f1d3SAlan Somers }
1399821f1d3SAlan Somers 
1409821f1d3SAlan Somers ProcessMockerT
14129edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in,
14229edc611SAlan Somers 				   struct mockfs_buf_out &out)> f)
1439821f1d3SAlan Somers {
14429edc611SAlan Somers 	return([=](auto& in, auto &out) {
14529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
14629edc611SAlan Somers 		out0->header.unique = in.header.unique;
14729edc611SAlan Somers 		f(in, *out0);
14829edc611SAlan Somers 		out.push_back(std::move(out0));
1499821f1d3SAlan Somers 	});
1509821f1d3SAlan Somers }
1519821f1d3SAlan Somers 
1529821f1d3SAlan Somers void sigint_handler(int __unused sig) {
1538b73a4c5SAlan Somers 	// Don't do anything except interrupt the daemon's read(2) call
1549821f1d3SAlan Somers }
1559821f1d3SAlan Somers 
156c2d70d6eSAlan Somers void MockFS::debug_request(const mockfs_buf_in &in)
1579821f1d3SAlan Somers {
15829edc611SAlan Somers 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
15929edc611SAlan Somers 		in.header.nodeid);
1609821f1d3SAlan Somers 	if (verbosity > 1) {
161cc04566cSAlan Somers 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u",
16229edc611SAlan Somers 			in.header.uid, in.header.gid, in.header.pid,
16329edc611SAlan Somers 			in.header.unique, in.header.len);
1649821f1d3SAlan Somers 	}
16529edc611SAlan Somers 	switch (in.header.opcode) {
16619ef317dSAlan Somers 		const char *name, *value;
16719ef317dSAlan Somers 
168caf5f57dSAlan Somers 		case FUSE_ACCESS:
16929edc611SAlan Somers 			printf(" mask=%#x", in.body.access.mask);
170caf5f57dSAlan Somers 			break;
171a1c9f4adSAlan Somers 		case FUSE_BMAP:
172*97b0512bSAlan Somers 			printf(" block=%" PRIx64 " blocksize=%#x",
173*97b0512bSAlan Somers 				in.body.bmap.block, in.body.bmap.blocksize);
174a1c9f4adSAlan Somers 			break;
17519ef317dSAlan Somers 		case FUSE_CREATE:
176a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
177a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
178a4856c96SAlan Somers 					sizeof(fuse_create_in);
179a4856c96SAlan Somers 			else
18029edc611SAlan Somers 				name = (const char*)in.body.bytes +
18119ef317dSAlan Somers 					sizeof(fuse_open_in);
18219ef317dSAlan Somers 			printf(" flags=%#x name=%s",
18329edc611SAlan Somers 				in.body.open.flags, name);
18419ef317dSAlan Somers 			break;
1859821f1d3SAlan Somers 		case FUSE_FLUSH:
186cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
18729edc611SAlan Somers 				in.body.flush.fh,
18829edc611SAlan Somers 				in.body.flush.lock_owner);
1899821f1d3SAlan Somers 			break;
1909821f1d3SAlan Somers 		case FUSE_FORGET:
19129edc611SAlan Somers 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
1929821f1d3SAlan Somers 			break;
1939821f1d3SAlan Somers 		case FUSE_FSYNC:
19429edc611SAlan Somers 			printf(" flags=%#x", in.body.fsync.fsync_flags);
1959821f1d3SAlan Somers 			break;
1969821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
19729edc611SAlan Somers 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
1989821f1d3SAlan Somers 			break;
199723c7768SAlan Somers 		case FUSE_INTERRUPT:
20029edc611SAlan Somers 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
201723c7768SAlan Somers 			break;
202002e54b0SAlan Somers 		case FUSE_LINK:
20329edc611SAlan Somers 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
204002e54b0SAlan Somers 			break;
2059821f1d3SAlan Somers 		case FUSE_LOOKUP:
20629edc611SAlan Somers 			printf(" %s", in.body.lookup);
2079821f1d3SAlan Somers 			break;
20899cf7bffSAlan Somers 		case FUSE_MKDIR:
20929edc611SAlan Somers 			name = (const char*)in.body.bytes +
21099cf7bffSAlan Somers 				sizeof(fuse_mkdir_in);
211a4856c96SAlan Somers 			printf(" name=%s mode=%#o umask=%#o", name,
212a4856c96SAlan Somers 				in.body.mkdir.mode, in.body.mkdir.umask);
21399cf7bffSAlan Somers 			break;
214bf4d7084SAlan Somers 		case FUSE_MKNOD:
215a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
216a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
217a4856c96SAlan Somers 					sizeof(fuse_mknod_in);
218a4856c96SAlan Somers 			else
219a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
220a4856c96SAlan Somers 					FUSE_COMPAT_MKNOD_IN_SIZE;
221a4856c96SAlan Somers 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
222a4856c96SAlan Somers 				in.body.mknod.mode, in.body.mknod.rdev,
223a4856c96SAlan Somers 				in.body.mknod.umask, name);
224bf4d7084SAlan Somers 			break;
2259821f1d3SAlan Somers 		case FUSE_OPEN:
226a4856c96SAlan Somers 			printf(" flags=%#x", in.body.open.flags);
2279821f1d3SAlan Somers 			break;
2289821f1d3SAlan Somers 		case FUSE_OPENDIR:
229a4856c96SAlan Somers 			printf(" flags=%#x", in.body.opendir.flags);
2309821f1d3SAlan Somers 			break;
2319821f1d3SAlan Somers 		case FUSE_READ:
232cc04566cSAlan Somers 			printf(" offset=%" PRIu64 " size=%u",
23329edc611SAlan Somers 				in.body.read.offset,
23429edc611SAlan Somers 				in.body.read.size);
235d4fd0c81SAlan Somers 			if (verbosity > 1)
236d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.read.flags);
2379821f1d3SAlan Somers 			break;
2389821f1d3SAlan Somers 		case FUSE_READDIR:
239cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
24029edc611SAlan Somers 				in.body.readdir.fh, in.body.readdir.offset,
24129edc611SAlan Somers 				in.body.readdir.size);
2429821f1d3SAlan Somers 			break;
2439821f1d3SAlan Somers 		case FUSE_RELEASE:
244cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
24529edc611SAlan Somers 				in.body.release.fh,
24629edc611SAlan Somers 				in.body.release.flags,
24729edc611SAlan Somers 				in.body.release.lock_owner);
2489821f1d3SAlan Somers 			break;
2499821f1d3SAlan Somers 		case FUSE_SETATTR:
2509821f1d3SAlan Somers 			if (verbosity <= 1) {
25129edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
2529821f1d3SAlan Somers 				break;
2539821f1d3SAlan Somers 			}
25429edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
25529edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
25629edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
25729edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
25829edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
25929edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
26029edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
26129edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
26229edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
263cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
26429edc611SAlan Somers 					in.body.setattr.atime,
26529edc611SAlan Somers 					in.body.setattr.atimensec);
26629edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
267cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
26829edc611SAlan Somers 					in.body.setattr.mtime,
26929edc611SAlan Somers 					in.body.setattr.mtimensec);
27029edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
27129edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
2729821f1d3SAlan Somers 			break;
273f067b609SAlan Somers 		case FUSE_SETLK:
274cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
275cc04566cSAlan Somers 				" type=%u pid=%u",
27629edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
27729edc611SAlan Somers 				in.body.setlk.lk.type,
27829edc611SAlan Somers 				in.body.setlk.lk.pid);
279f067b609SAlan Somers 			if (verbosity >= 2) {
280cc04566cSAlan Somers 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
28129edc611SAlan Somers 					in.body.setlk.lk.start,
28229edc611SAlan Somers 					in.body.setlk.lk.end);
283f067b609SAlan Somers 			}
284f067b609SAlan Somers 			break;
2859821f1d3SAlan Somers 		case FUSE_SETXATTR:
2869821f1d3SAlan Somers 			/*
2879821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
2889821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
2899821f1d3SAlan Somers 			 */
29029edc611SAlan Somers 			name = (const char*)in.body.bytes +
2919821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
29219ef317dSAlan Somers 			value = name + strlen(name) + 1;
29319ef317dSAlan Somers 			printf(" %s=%s", name, value);
2949821f1d3SAlan Somers 			break;
2959821f1d3SAlan Somers 		case FUSE_WRITE:
296cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
297d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
29829edc611SAlan Somers 				in.body.write.fh,
29929edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
30029edc611SAlan Somers 				in.body.write.write_flags);
301d4fd0c81SAlan Somers 			if (verbosity > 1)
302d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
3039821f1d3SAlan Somers 			break;
3049821f1d3SAlan Somers 		default:
3059821f1d3SAlan Somers 			break;
3069821f1d3SAlan Somers 	}
3079821f1d3SAlan Somers 	printf("\n");
3089821f1d3SAlan Somers }
3099821f1d3SAlan Somers 
310c2d70d6eSAlan Somers /*
311c2d70d6eSAlan Somers  * Debug a FUSE response.
312c2d70d6eSAlan Somers  *
313c2d70d6eSAlan Somers  * This is mostly useful for asynchronous notifications, which don't correspond
314c2d70d6eSAlan Somers  * to any request
315c2d70d6eSAlan Somers  */
316c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
317c2d70d6eSAlan Somers 	const char *name;
318c2d70d6eSAlan Somers 
319c2d70d6eSAlan Somers 	if (verbosity == 0)
320c2d70d6eSAlan Somers 		return;
321c2d70d6eSAlan Somers 
322c2d70d6eSAlan Somers 	switch (out.header.error) {
323c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
324c2d70d6eSAlan Somers 			name = (const char*)out.body.bytes +
325c2d70d6eSAlan Somers 				sizeof(fuse_notify_inval_entry_out);
326c2d70d6eSAlan Somers 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
327c2d70d6eSAlan Somers 				out.body.inval_entry.parent, name);
328c2d70d6eSAlan Somers 			break;
329eae1ae13SAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
330eae1ae13SAlan Somers 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
331eae1ae13SAlan Somers 				" len=%" PRIi64 "\n",
332eae1ae13SAlan Somers 				out.body.inval_inode.ino,
333eae1ae13SAlan Somers 				out.body.inval_inode.off,
334eae1ae13SAlan Somers 				out.body.inval_inode.len);
335eae1ae13SAlan Somers 			break;
3367cbb8e8aSAlan Somers 		case FUSE_NOTIFY_STORE:
3377cbb8e8aSAlan Somers 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
3387cbb8e8aSAlan Somers 				" size=%" PRIu32 "\n",
3397cbb8e8aSAlan Somers 				out.body.store.nodeid,
3407cbb8e8aSAlan Somers 				out.body.store.offset,
3417cbb8e8aSAlan Somers 				out.body.store.size);
3427cbb8e8aSAlan Somers 			break;
343c2d70d6eSAlan Somers 		default:
344c2d70d6eSAlan Somers 			break;
345c2d70d6eSAlan Somers 	}
346c2d70d6eSAlan Somers }
347c2d70d6eSAlan Somers 
34891ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
34916bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
350402b609cSAlan Somers 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
351fef46454SAlan Somers 	bool noclusterr, unsigned time_gran)
3529821f1d3SAlan Somers {
3538b73a4c5SAlan Somers 	struct sigaction sa;
3549821f1d3SAlan Somers 	struct iovec *iov = NULL;
3559821f1d3SAlan Somers 	int iovlen = 0;
3569821f1d3SAlan Somers 	char fdstr[15];
35791ff3a0dSAlan Somers 	const bool trueval = true;
3589821f1d3SAlan Somers 
3599821f1d3SAlan Somers 	m_daemon_id = NULL;
36016bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
3619821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
3628eecd9ceSAlan Somers 	m_maxwrite = max_write;
3630a7c63e0SAlan Somers 	m_nready = -1;
3643429092cSAlan Somers 	m_pm = pm;
365fef46454SAlan Somers 	m_time_gran = time_gran;
36681a619c4SAlan Somers 	m_quit = false;
3673429092cSAlan Somers 	if (m_pm == KQ)
3683429092cSAlan Somers 		m_kq = kqueue();
3693429092cSAlan Somers 	else
3703429092cSAlan Somers 		m_kq = -1;
3719821f1d3SAlan Somers 
3729821f1d3SAlan Somers 	/*
3739821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
3749821f1d3SAlan Somers 	 * mkdtemp
3759821f1d3SAlan Somers 	 */
3769821f1d3SAlan Somers 	/*
3779821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
3789821f1d3SAlan Somers 	 * instead.
3799821f1d3SAlan Somers 	 */
38091ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
3819821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3829821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
3839821f1d3SAlan Somers 
3843429092cSAlan Somers 	switch (m_pm) {
3853429092cSAlan Somers 	case BLOCKING:
38691ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
3873429092cSAlan Somers 		break;
3883429092cSAlan Somers 	default:
3893429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
3903429092cSAlan Somers 		break;
3913429092cSAlan Somers 	}
3929821f1d3SAlan Somers 	if (m_fuse_fd < 0)
3939821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3949821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
3959821f1d3SAlan Somers 
3969821f1d3SAlan Somers 	m_pid = getpid();
39791ff3a0dSAlan Somers 	m_child_pid = -1;
3989821f1d3SAlan Somers 
3999821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4009821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
4019821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
4029821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4033429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
4049821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
40591ff3a0dSAlan Somers 	if (allow_other) {
40691ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
4079821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4089821f1d3SAlan Somers 	}
4099821f1d3SAlan Somers 	if (default_permissions) {
4109821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
4119821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4129821f1d3SAlan Somers 	}
41391ff3a0dSAlan Somers 	if (push_symlinks_in) {
41491ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
41591ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
41691ff3a0dSAlan Somers 	}
417140bb492SAlan Somers 	if (ro) {
418140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
419140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
420140bb492SAlan Somers 	}
4218eecd9ceSAlan Somers 	if (async) {
4228eecd9ceSAlan Somers 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4238eecd9ceSAlan Somers 			sizeof(bool));
4248eecd9ceSAlan Somers 	}
425402b609cSAlan Somers 	if (noclusterr) {
426402b609cSAlan Somers 		build_iovec(&iov, &iovlen, "noclusterr",
427402b609cSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
428402b609cSAlan Somers 	}
4299821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
4309821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4319821f1d3SAlan Somers 			"Couldn't mount filesystem"));
4329821f1d3SAlan Somers 
4339821f1d3SAlan Somers 	// Setup default handler
4349821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
4359821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
4369821f1d3SAlan Somers 
4379821f1d3SAlan Somers 	init(flags);
4388b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
4398b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
4408b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
4418b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
4428b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4438b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
4449821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
4459821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4469821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
4479821f1d3SAlan Somers }
4489821f1d3SAlan Somers 
4499821f1d3SAlan Somers MockFS::~MockFS() {
4509821f1d3SAlan Somers 	kill_daemon();
4519821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
4529821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
4539821f1d3SAlan Somers 		m_daemon_id = NULL;
4549821f1d3SAlan Somers 	}
4558b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
4569821f1d3SAlan Somers 	rmdir("mountpoint");
4573429092cSAlan Somers 	if (m_kq >= 0)
4583429092cSAlan Somers 		close(m_kq);
4599821f1d3SAlan Somers }
4609821f1d3SAlan Somers 
4619821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
46229edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
46329edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
4649821f1d3SAlan Somers 
46529edc611SAlan Somers 	read_request(*in);
4669821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
4679821f1d3SAlan Somers 
4689821f1d3SAlan Somers 	out->header.unique = in->header.unique;
4699821f1d3SAlan Somers 	out->header.error = 0;
4709821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
47116bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
4729821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
4738eecd9ceSAlan Somers 	out->body.init.max_write = m_maxwrite;
4749821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
47587ff949aSAlan Somers 
47687ff949aSAlan Somers 	if (m_kernel_minor_version < 23) {
47787ff949aSAlan Somers 		SET_OUT_HEADER_LEN(*out, init_7_22);
47887ff949aSAlan Somers 	} else {
479fef46454SAlan Somers 		out->body.init.time_gran = m_time_gran;
48029edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out, init);
48187ff949aSAlan Somers 	}
48287ff949aSAlan Somers 
48329edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
4849821f1d3SAlan Somers }
4859821f1d3SAlan Somers 
4869821f1d3SAlan Somers void MockFS::kill_daemon() {
48781a619c4SAlan Somers 	m_quit = true;
4888b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
4899821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
4908b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
4918b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
4928b73a4c5SAlan Somers 	// during the unmount sequence.
4939821f1d3SAlan Somers 	close(m_fuse_fd);
4948b73a4c5SAlan Somers 	m_fuse_fd = -1;
4959821f1d3SAlan Somers }
4969821f1d3SAlan Somers 
4979821f1d3SAlan Somers void MockFS::loop() {
49829edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
4999821f1d3SAlan Somers 
50029edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
5019821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
50281a619c4SAlan Somers 	while (!m_quit) {
50329edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
50429edc611SAlan Somers 		read_request(*in);
50581a619c4SAlan Somers 		if (m_quit)
5069821f1d3SAlan Somers 			break;
5079821f1d3SAlan Somers 		if (verbosity > 0)
508c2d70d6eSAlan Somers 			debug_request(*in);
5099821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
51029edc611SAlan Somers 			process(*in, out);
5119821f1d3SAlan Somers 		} else {
5129821f1d3SAlan Somers 			/*
5139821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
5149821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
5159821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
5169821f1d3SAlan Somers 			 */
51799cf7bffSAlan Somers 			if (verbosity > 1)
51899cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
51999cf7bffSAlan Somers 					in->header.pid);
52029edc611SAlan Somers 			process_default(*in, out);
5219821f1d3SAlan Somers 		}
52229edc611SAlan Somers 		for (auto &it: out)
52329edc611SAlan Somers 			write_response(*it);
5249821f1d3SAlan Somers 		out.clear();
5259821f1d3SAlan Somers 	}
5269821f1d3SAlan Somers }
5279821f1d3SAlan Somers 
528c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
529c2d70d6eSAlan Somers {
530c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
531c2d70d6eSAlan Somers 
532c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
533c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
534c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
535c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
536c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
537c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
538c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
539c2d70d6eSAlan Somers 		namelen;
540c2d70d6eSAlan Somers 	debug_response(*out);
541c2d70d6eSAlan Somers 	write_response(*out);
542c2d70d6eSAlan Somers 	return 0;
543c2d70d6eSAlan Somers }
544c2d70d6eSAlan Somers 
545eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
546eae1ae13SAlan Somers {
547eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
548eae1ae13SAlan Somers 
549eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
550eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
551eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
552eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
553eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
554eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
555eae1ae13SAlan Somers 	debug_response(*out);
556eae1ae13SAlan Somers 	write_response(*out);
557eae1ae13SAlan Somers 	return 0;
558eae1ae13SAlan Somers }
559eae1ae13SAlan Somers 
5607cbb8e8aSAlan Somers int MockFS::notify_store(ino_t ino, off_t off, void* data, ssize_t size)
5617cbb8e8aSAlan Somers {
5627cbb8e8aSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
5637cbb8e8aSAlan Somers 
5647cbb8e8aSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
5657cbb8e8aSAlan Somers 	out->header.error = FUSE_NOTIFY_STORE;
5667cbb8e8aSAlan Somers 	out->body.store.nodeid = ino;
5677cbb8e8aSAlan Somers 	out->body.store.offset = off;
5687cbb8e8aSAlan Somers 	out->body.store.size = size;
5697cbb8e8aSAlan Somers 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
5707cbb8e8aSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
5717cbb8e8aSAlan Somers 	debug_response(*out);
5727cbb8e8aSAlan Somers 	write_response(*out);
5737cbb8e8aSAlan Somers 	return 0;
5747cbb8e8aSAlan Somers }
5757cbb8e8aSAlan Somers 
5769821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
5779821f1d3SAlan Somers 	if (pid == m_pid) {
5789821f1d3SAlan Somers 		return (true);
57991ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
58091ff3a0dSAlan Somers 		return (true);
5819821f1d3SAlan Somers 	} else {
5829821f1d3SAlan Somers 		struct kinfo_proc *ki;
5839821f1d3SAlan Somers 		bool ok = false;
5849821f1d3SAlan Somers 
5859821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
5869821f1d3SAlan Somers 		if (ki == NULL)
5879821f1d3SAlan Somers 			return (false);
5889821f1d3SAlan Somers 		/*
5899821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
5909821f1d3SAlan Somers 		 * can use aio functions
5919821f1d3SAlan Somers 		 */
5929821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
5939821f1d3SAlan Somers 			ok = true;
5949821f1d3SAlan Somers 		free(ki);
5959821f1d3SAlan Somers 		return (ok);
5969821f1d3SAlan Somers 	}
5979821f1d3SAlan Somers }
5989821f1d3SAlan Somers 
59929edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
60029edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
6019821f1d3SAlan Somers {
60229edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
60329edc611SAlan Somers 	out0->header.unique = in.header.unique;
6049821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
6059821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
60629edc611SAlan Somers 	out.push_back(std::move(out0));
6079821f1d3SAlan Somers }
6089821f1d3SAlan Somers 
60929edc611SAlan Somers void MockFS::read_request(mockfs_buf_in &in) {
6109821f1d3SAlan Somers 	ssize_t res;
61177fbe694SAlan Somers 	int nready = 0;
6123429092cSAlan Somers 	fd_set readfds;
6133429092cSAlan Somers 	pollfd fds[1];
6143429092cSAlan Somers 	struct kevent changes[1];
6153429092cSAlan Somers 	struct kevent events[1];
61677fbe694SAlan Somers 	struct timespec timeout_ts;
61777fbe694SAlan Somers 	struct timeval timeout_tv;
61877fbe694SAlan Somers 	const int timeout_ms = 999;
61977fbe694SAlan Somers 	int timeout_int, nfds;
6209821f1d3SAlan Somers 
6213429092cSAlan Somers 	switch (m_pm) {
6223429092cSAlan Somers 	case BLOCKING:
6233429092cSAlan Somers 		break;
6243429092cSAlan Somers 	case KQ:
62577fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
62677fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
62777fbe694SAlan Somers 		while (nready == 0) {
62877fbe694SAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
62977fbe694SAlan Somers 				0, 0);
63077fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
63177fbe694SAlan Somers 				&timeout_ts);
6323429092cSAlan Somers 			if (m_quit)
6333429092cSAlan Somers 				return;
63477fbe694SAlan Somers 		}
6353429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6363429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
6373429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
6383429092cSAlan Somers 			FAIL() << strerror(events[0].data);
6393429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
6403429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
6410a7c63e0SAlan Somers 		m_nready = events[0].data;
6423429092cSAlan Somers 		break;
6433429092cSAlan Somers 	case POLL:
64477fbe694SAlan Somers 		timeout_int = timeout_ms;
6453429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
6463429092cSAlan Somers 		fds[0].events = POLLIN;
64777fbe694SAlan Somers 		while (nready == 0) {
64877fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
6493429092cSAlan Somers 			if (m_quit)
6503429092cSAlan Somers 				return;
65177fbe694SAlan Somers 		}
6523429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6533429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
6543429092cSAlan Somers 		break;
6553429092cSAlan Somers 	case SELECT:
65677fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
65777fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
65877fbe694SAlan Somers 		nfds = m_fuse_fd + 1;
65977fbe694SAlan Somers 		while (nready == 0) {
6603429092cSAlan Somers 			FD_ZERO(&readfds);
6613429092cSAlan Somers 			FD_SET(m_fuse_fd, &readfds);
66277fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
66377fbe694SAlan Somers 				&timeout_tv);
6643429092cSAlan Somers 			if (m_quit)
6653429092cSAlan Somers 				return;
66677fbe694SAlan Somers 		}
6673429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6683429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
6693429092cSAlan Somers 		break;
6703429092cSAlan Somers 	default:
6713429092cSAlan Somers 		FAIL() << "not yet implemented";
6723429092cSAlan Somers 	}
67329edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
6743429092cSAlan Somers 
675b690d120SAlan Somers 	if (res < 0 && !m_quit) {
676b690d120SAlan Somers 		FAIL() << "read: " << strerror(errno);
677b690d120SAlan Somers 		m_quit = true;
678b690d120SAlan Somers 	}
67929edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
6809821f1d3SAlan Somers }
6819821f1d3SAlan Somers 
68229edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
6833429092cSAlan Somers 	fd_set writefds;
6843429092cSAlan Somers 	pollfd fds[1];
6853429092cSAlan Somers 	int nready, nfds;
6863429092cSAlan Somers 	ssize_t r;
6873429092cSAlan Somers 
6883429092cSAlan Somers 	switch (m_pm) {
6893429092cSAlan Somers 	case BLOCKING:
6903429092cSAlan Somers 	case KQ:	/* EVFILT_WRITE is not supported */
6913429092cSAlan Somers 		break;
6923429092cSAlan Somers 	case POLL:
6933429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
6943429092cSAlan Somers 		fds[0].events = POLLOUT;
6953429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
6963429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6973429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
6983429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
6993429092cSAlan Somers 		break;
7003429092cSAlan Somers 	case SELECT:
7013429092cSAlan Somers 		FD_ZERO(&writefds);
7023429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
7033429092cSAlan Somers 		nfds = m_fuse_fd + 1;
7043429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
7053429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
7063429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
7073429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
7083429092cSAlan Somers 		break;
7093429092cSAlan Somers 	default:
7103429092cSAlan Somers 		FAIL() << "not yet implemented";
7113429092cSAlan Somers 	}
71229edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
7133429092cSAlan Somers 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
7143429092cSAlan Somers }
7153429092cSAlan Somers 
7169821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
7179821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
7189821f1d3SAlan Somers 
7199821f1d3SAlan Somers 	mock_fs->loop();
7209821f1d3SAlan Somers 
7219821f1d3SAlan Somers 	return (NULL);
7229821f1d3SAlan Somers }
7239821f1d3SAlan Somers 
7249821f1d3SAlan Somers void MockFS::unmount() {
7259821f1d3SAlan Somers 	::unmount("mountpoint", 0);
7269821f1d3SAlan Somers }
727