xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision c2d342c509065bee6392624e95b75cf7b3bb9c45)
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 {
6537df9d3bSAlan 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",
10437df9d3bSAlan Somers 		"DESTROY",
10537df9d3bSAlan Somers 		"IOCTL",
10637df9d3bSAlan Somers 		"POLL",
10737df9d3bSAlan Somers 		"NOTIFY_REPLY",
10837df9d3bSAlan Somers 		"BATCH_FORGET",
10937df9d3bSAlan Somers 		"FALLOCATE",
11037df9d3bSAlan Somers 		"READDIRPLUS",
11137df9d3bSAlan Somers 		"RENAME2",
11237df9d3bSAlan Somers 		"LSEEK",
11392bbfe1fSAlan Somers 		"COPY_FILE_RANGE",
1149821f1d3SAlan Somers 	};
11537df9d3bSAlan Somers 	if (opcode >= nitems(table))
1169821f1d3SAlan Somers 		return ("Unknown (opcode > max)");
1179821f1d3SAlan Somers 	else
1189821f1d3SAlan Somers 		return (table[opcode]);
1199821f1d3SAlan Somers }
1209821f1d3SAlan Somers 
1219821f1d3SAlan Somers ProcessMockerT
1229821f1d3SAlan Somers ReturnErrno(int error)
1239821f1d3SAlan Somers {
1249821f1d3SAlan Somers 	return([=](auto in, auto &out) {
12529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
12629edc611SAlan Somers 		out0->header.unique = in.header.unique;
1279821f1d3SAlan Somers 		out0->header.error = -error;
1289821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
12929edc611SAlan Somers 		out.push_back(std::move(out0));
1309821f1d3SAlan Somers 	});
1319821f1d3SAlan Somers }
1329821f1d3SAlan Somers 
1339821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
1349821f1d3SAlan Somers ProcessMockerT
1359821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
1369821f1d3SAlan Somers {
1379821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1389821f1d3SAlan Somers 		/* nodeid means ENOENT and cache it */
13929edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
1409821f1d3SAlan Somers 		out0->body.entry.nodeid = 0;
14129edc611SAlan Somers 		out0->header.unique = in.header.unique;
1429821f1d3SAlan Somers 		out0->header.error = 0;
1439821f1d3SAlan Somers 		out0->body.entry.entry_valid = entry_valid->tv_sec;
1449821f1d3SAlan Somers 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
14529edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
14629edc611SAlan Somers 		out.push_back(std::move(out0));
1479821f1d3SAlan Somers 	});
1489821f1d3SAlan Somers }
1499821f1d3SAlan Somers 
1509821f1d3SAlan Somers ProcessMockerT
15129edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in,
15229edc611SAlan Somers 				   struct mockfs_buf_out &out)> f)
1539821f1d3SAlan Somers {
15429edc611SAlan Somers 	return([=](auto& in, auto &out) {
15529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
15629edc611SAlan Somers 		out0->header.unique = in.header.unique;
15729edc611SAlan Somers 		f(in, *out0);
15829edc611SAlan Somers 		out.push_back(std::move(out0));
1599821f1d3SAlan Somers 	});
1609821f1d3SAlan Somers }
1619821f1d3SAlan Somers 
1629821f1d3SAlan Somers void sigint_handler(int __unused sig) {
1638b73a4c5SAlan Somers 	// Don't do anything except interrupt the daemon's read(2) call
1649821f1d3SAlan Somers }
1659821f1d3SAlan Somers 
1666c0c3620SAlan Somers void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
1679821f1d3SAlan Somers {
16829edc611SAlan Somers 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
16929edc611SAlan Somers 		in.header.nodeid);
1709821f1d3SAlan Somers 	if (verbosity > 1) {
1716c0c3620SAlan Somers 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
1726c0c3620SAlan Somers 			" buflen=%zd",
17329edc611SAlan Somers 			in.header.uid, in.header.gid, in.header.pid,
1746c0c3620SAlan Somers 			in.header.unique, in.header.len, buflen);
1759821f1d3SAlan Somers 	}
17629edc611SAlan Somers 	switch (in.header.opcode) {
17719ef317dSAlan Somers 		const char *name, *value;
17819ef317dSAlan Somers 
179caf5f57dSAlan Somers 		case FUSE_ACCESS:
18029edc611SAlan Somers 			printf(" mask=%#x", in.body.access.mask);
181caf5f57dSAlan Somers 			break;
182a1c9f4adSAlan Somers 		case FUSE_BMAP:
18397b0512bSAlan Somers 			printf(" block=%" PRIx64 " blocksize=%#x",
18497b0512bSAlan Somers 				in.body.bmap.block, in.body.bmap.blocksize);
185a1c9f4adSAlan Somers 			break;
18692bbfe1fSAlan Somers 		case FUSE_COPY_FILE_RANGE:
18792bbfe1fSAlan Somers 			printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
18892bbfe1fSAlan Somers 			       " off_out=%" PRIu64 " size=%" PRIu64,
18992bbfe1fSAlan Somers 			       in.body.copy_file_range.off_in,
19092bbfe1fSAlan Somers 			       in.body.copy_file_range.nodeid_out,
19192bbfe1fSAlan Somers 			       in.body.copy_file_range.off_out,
19292bbfe1fSAlan Somers 			       in.body.copy_file_range.len);
19392bbfe1fSAlan Somers 			if (verbosity > 1)
19492bbfe1fSAlan Somers 				printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
19592bbfe1fSAlan Somers 				       " flags=%" PRIx64,
19692bbfe1fSAlan Somers 				       in.body.copy_file_range.fh_in,
19792bbfe1fSAlan Somers 				       in.body.copy_file_range.fh_out,
19892bbfe1fSAlan Somers 				       in.body.copy_file_range.flags);
19992bbfe1fSAlan Somers 			break;
20019ef317dSAlan Somers 		case FUSE_CREATE:
201a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
202a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
203a4856c96SAlan Somers 					sizeof(fuse_create_in);
204a4856c96SAlan Somers 			else
20529edc611SAlan Somers 				name = (const char*)in.body.bytes +
20619ef317dSAlan Somers 					sizeof(fuse_open_in);
20719ef317dSAlan Somers 			printf(" flags=%#x name=%s",
20829edc611SAlan Somers 				in.body.open.flags, name);
20919ef317dSAlan Somers 			break;
2109821f1d3SAlan Somers 		case FUSE_FLUSH:
211cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
21229edc611SAlan Somers 				in.body.flush.fh,
21329edc611SAlan Somers 				in.body.flush.lock_owner);
2149821f1d3SAlan Somers 			break;
2159821f1d3SAlan Somers 		case FUSE_FORGET:
21629edc611SAlan Somers 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
2179821f1d3SAlan Somers 			break;
2189821f1d3SAlan Somers 		case FUSE_FSYNC:
21929edc611SAlan Somers 			printf(" flags=%#x", in.body.fsync.fsync_flags);
2209821f1d3SAlan Somers 			break;
2219821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
22229edc611SAlan Somers 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
2239821f1d3SAlan Somers 			break;
224723c7768SAlan Somers 		case FUSE_INTERRUPT:
22529edc611SAlan Somers 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
226723c7768SAlan Somers 			break;
227002e54b0SAlan Somers 		case FUSE_LINK:
22829edc611SAlan Somers 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
229002e54b0SAlan Somers 			break;
2305e633330SAlan Somers 		case FUSE_LISTXATTR:
2315e633330SAlan Somers 			printf(" size=%" PRIu32, in.body.listxattr.size);
2325e633330SAlan Somers 			break;
2339821f1d3SAlan Somers 		case FUSE_LOOKUP:
23429edc611SAlan Somers 			printf(" %s", in.body.lookup);
2359821f1d3SAlan Somers 			break;
23637df9d3bSAlan Somers 		case FUSE_LSEEK:
23737df9d3bSAlan Somers 			switch (in.body.lseek.whence) {
23837df9d3bSAlan Somers 			case SEEK_HOLE:
2391d010cd3SCy Schubert 				printf(" SEEK_HOLE offset=%jd",
24037df9d3bSAlan Somers 				    in.body.lseek.offset);
24137df9d3bSAlan Somers 				break;
24237df9d3bSAlan Somers 			case SEEK_DATA:
2431d010cd3SCy Schubert 				printf(" SEEK_DATA offset=%jd",
24437df9d3bSAlan Somers 				    in.body.lseek.offset);
24537df9d3bSAlan Somers 				break;
24637df9d3bSAlan Somers 			default:
2471d010cd3SCy Schubert 				printf(" whence=%u offset=%jd",
24837df9d3bSAlan Somers 				    in.body.lseek.whence, in.body.lseek.offset);
24937df9d3bSAlan Somers 				break;
25037df9d3bSAlan Somers 			}
25137df9d3bSAlan Somers 			break;
25299cf7bffSAlan Somers 		case FUSE_MKDIR:
25329edc611SAlan Somers 			name = (const char*)in.body.bytes +
25499cf7bffSAlan Somers 				sizeof(fuse_mkdir_in);
255a4856c96SAlan Somers 			printf(" name=%s mode=%#o umask=%#o", name,
256a4856c96SAlan Somers 				in.body.mkdir.mode, in.body.mkdir.umask);
25799cf7bffSAlan Somers 			break;
258bf4d7084SAlan Somers 		case FUSE_MKNOD:
259a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
260a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
261a4856c96SAlan Somers 					sizeof(fuse_mknod_in);
262a4856c96SAlan Somers 			else
263a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
264a4856c96SAlan Somers 					FUSE_COMPAT_MKNOD_IN_SIZE;
265a4856c96SAlan Somers 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
266a4856c96SAlan Somers 				in.body.mknod.mode, in.body.mknod.rdev,
267a4856c96SAlan Somers 				in.body.mknod.umask, name);
268bf4d7084SAlan Somers 			break;
2699821f1d3SAlan Somers 		case FUSE_OPEN:
270a4856c96SAlan Somers 			printf(" flags=%#x", in.body.open.flags);
2719821f1d3SAlan Somers 			break;
2729821f1d3SAlan Somers 		case FUSE_OPENDIR:
273a4856c96SAlan Somers 			printf(" flags=%#x", in.body.opendir.flags);
2749821f1d3SAlan Somers 			break;
2759821f1d3SAlan Somers 		case FUSE_READ:
276cc04566cSAlan Somers 			printf(" offset=%" PRIu64 " size=%u",
27729edc611SAlan Somers 				in.body.read.offset,
27829edc611SAlan Somers 				in.body.read.size);
279d4fd0c81SAlan Somers 			if (verbosity > 1)
280d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.read.flags);
2819821f1d3SAlan Somers 			break;
2829821f1d3SAlan Somers 		case FUSE_READDIR:
283cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
28429edc611SAlan Somers 				in.body.readdir.fh, in.body.readdir.offset,
28529edc611SAlan Somers 				in.body.readdir.size);
2869821f1d3SAlan Somers 			break;
2879821f1d3SAlan Somers 		case FUSE_RELEASE:
288cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
28929edc611SAlan Somers 				in.body.release.fh,
29029edc611SAlan Somers 				in.body.release.flags,
29129edc611SAlan Somers 				in.body.release.lock_owner);
2929821f1d3SAlan Somers 			break;
293*c2d342c5SAlan Somers 		case FUSE_RENAME:
294*c2d342c5SAlan Somers 			{
295*c2d342c5SAlan Somers 				const char *src = (const char*)in.body.bytes +
296*c2d342c5SAlan Somers 					sizeof(fuse_rename_in);
297*c2d342c5SAlan Somers 				const char *dst = src + strlen(src) + 1;
298*c2d342c5SAlan Somers 				printf(" src=%s newdir=%" PRIu64 " dst=%s",
299*c2d342c5SAlan Somers 					src, in.body.rename.newdir, dst);
300*c2d342c5SAlan Somers 			}
301*c2d342c5SAlan Somers 			break;
3029821f1d3SAlan Somers 		case FUSE_SETATTR:
3039821f1d3SAlan Somers 			if (verbosity <= 1) {
30429edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
3059821f1d3SAlan Somers 				break;
3069821f1d3SAlan Somers 			}
30729edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
30829edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
30929edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
31029edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
31129edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
31229edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
31329edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
31429edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
31529edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
316cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
31729edc611SAlan Somers 					in.body.setattr.atime,
31829edc611SAlan Somers 					in.body.setattr.atimensec);
31929edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
320cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
32129edc611SAlan Somers 					in.body.setattr.mtime,
32229edc611SAlan Somers 					in.body.setattr.mtimensec);
32329edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
32429edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
3259821f1d3SAlan Somers 			break;
326f067b609SAlan Somers 		case FUSE_SETLK:
327cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
328cc04566cSAlan Somers 				" type=%u pid=%u",
32929edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
33029edc611SAlan Somers 				in.body.setlk.lk.type,
33129edc611SAlan Somers 				in.body.setlk.lk.pid);
332f067b609SAlan Somers 			if (verbosity >= 2) {
333cc04566cSAlan Somers 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
33429edc611SAlan Somers 					in.body.setlk.lk.start,
33529edc611SAlan Somers 					in.body.setlk.lk.end);
336f067b609SAlan Somers 			}
337f067b609SAlan Somers 			break;
3389821f1d3SAlan Somers 		case FUSE_SETXATTR:
3399821f1d3SAlan Somers 			/*
3409821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
3419821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
3429821f1d3SAlan Somers 			 */
34329edc611SAlan Somers 			name = (const char*)in.body.bytes +
3449821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
34519ef317dSAlan Somers 			value = name + strlen(name) + 1;
34619ef317dSAlan Somers 			printf(" %s=%s", name, value);
3479821f1d3SAlan Somers 			break;
3489821f1d3SAlan Somers 		case FUSE_WRITE:
349cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
350d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
35129edc611SAlan Somers 				in.body.write.fh,
35229edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
35329edc611SAlan Somers 				in.body.write.write_flags);
354d4fd0c81SAlan Somers 			if (verbosity > 1)
355d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
3569821f1d3SAlan Somers 			break;
3579821f1d3SAlan Somers 		default:
3589821f1d3SAlan Somers 			break;
3599821f1d3SAlan Somers 	}
3609821f1d3SAlan Somers 	printf("\n");
3619821f1d3SAlan Somers }
3629821f1d3SAlan Somers 
363c2d70d6eSAlan Somers /*
364c2d70d6eSAlan Somers  * Debug a FUSE response.
365c2d70d6eSAlan Somers  *
366c2d70d6eSAlan Somers  * This is mostly useful for asynchronous notifications, which don't correspond
367c2d70d6eSAlan Somers  * to any request
368c2d70d6eSAlan Somers  */
369c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
370c2d70d6eSAlan Somers 	const char *name;
371c2d70d6eSAlan Somers 
372c2d70d6eSAlan Somers 	if (verbosity == 0)
373c2d70d6eSAlan Somers 		return;
374c2d70d6eSAlan Somers 
375c2d70d6eSAlan Somers 	switch (out.header.error) {
376c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
377c2d70d6eSAlan Somers 			name = (const char*)out.body.bytes +
378c2d70d6eSAlan Somers 				sizeof(fuse_notify_inval_entry_out);
379c2d70d6eSAlan Somers 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
380c2d70d6eSAlan Somers 				out.body.inval_entry.parent, name);
381c2d70d6eSAlan Somers 			break;
382eae1ae13SAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
383eae1ae13SAlan Somers 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
384eae1ae13SAlan Somers 				" len=%" PRIi64 "\n",
385eae1ae13SAlan Somers 				out.body.inval_inode.ino,
386eae1ae13SAlan Somers 				out.body.inval_inode.off,
387eae1ae13SAlan Somers 				out.body.inval_inode.len);
388eae1ae13SAlan Somers 			break;
3897cbb8e8aSAlan Somers 		case FUSE_NOTIFY_STORE:
3907cbb8e8aSAlan Somers 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
3917cbb8e8aSAlan Somers 				" size=%" PRIu32 "\n",
3927cbb8e8aSAlan Somers 				out.body.store.nodeid,
3937cbb8e8aSAlan Somers 				out.body.store.offset,
3947cbb8e8aSAlan Somers 				out.body.store.size);
3957cbb8e8aSAlan Somers 			break;
396c2d70d6eSAlan Somers 		default:
397c2d70d6eSAlan Somers 			break;
398c2d70d6eSAlan Somers 	}
399c2d70d6eSAlan Somers }
400c2d70d6eSAlan Somers 
40191ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
40216bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
403402b609cSAlan Somers 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
40491972cfcSAlan Somers 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime)
4059821f1d3SAlan Somers {
4068b73a4c5SAlan Somers 	struct sigaction sa;
4079821f1d3SAlan Somers 	struct iovec *iov = NULL;
4089821f1d3SAlan Somers 	int iovlen = 0;
4099821f1d3SAlan Somers 	char fdstr[15];
41091ff3a0dSAlan Somers 	const bool trueval = true;
4119821f1d3SAlan Somers 
4129821f1d3SAlan Somers 	m_daemon_id = NULL;
41316bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
4149821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
415f928dbcbSAlan Somers 	m_maxwrite = MIN(max_write, max_max_write);
4160a7c63e0SAlan Somers 	m_nready = -1;
4173429092cSAlan Somers 	m_pm = pm;
418fef46454SAlan Somers 	m_time_gran = time_gran;
41981a619c4SAlan Somers 	m_quit = false;
4205403f2c1SAlan Somers 	m_last_unique = 0;
4213429092cSAlan Somers 	if (m_pm == KQ)
4223429092cSAlan Somers 		m_kq = kqueue();
4233429092cSAlan Somers 	else
4243429092cSAlan Somers 		m_kq = -1;
4259821f1d3SAlan Somers 
4269821f1d3SAlan Somers 	/*
4279821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
4289821f1d3SAlan Somers 	 * mkdtemp
4299821f1d3SAlan Somers 	 */
4309821f1d3SAlan Somers 	/*
4319821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
4329821f1d3SAlan Somers 	 * instead.
4339821f1d3SAlan Somers 	 */
43491ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
4359821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4369821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
4379821f1d3SAlan Somers 
4383429092cSAlan Somers 	switch (m_pm) {
4393429092cSAlan Somers 	case BLOCKING:
44091ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
4413429092cSAlan Somers 		break;
4423429092cSAlan Somers 	default:
4433429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
4443429092cSAlan Somers 		break;
4453429092cSAlan Somers 	}
4469821f1d3SAlan Somers 	if (m_fuse_fd < 0)
4479821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4489821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
4499821f1d3SAlan Somers 
4509821f1d3SAlan Somers 	m_pid = getpid();
45191ff3a0dSAlan Somers 	m_child_pid = -1;
4529821f1d3SAlan Somers 
4539821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4549821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
4559821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
4569821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4573429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
4589821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
45991ff3a0dSAlan Somers 	if (allow_other) {
46091ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
4619821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4629821f1d3SAlan Somers 	}
4639821f1d3SAlan Somers 	if (default_permissions) {
4649821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
4659821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4669821f1d3SAlan Somers 	}
46791ff3a0dSAlan Somers 	if (push_symlinks_in) {
46891ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
46991ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
47091ff3a0dSAlan Somers 	}
471140bb492SAlan Somers 	if (ro) {
472140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
473140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
474140bb492SAlan Somers 	}
4758eecd9ceSAlan Somers 	if (async) {
4768eecd9ceSAlan Somers 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4778eecd9ceSAlan Somers 			sizeof(bool));
4788eecd9ceSAlan Somers 	}
47991972cfcSAlan Somers 	if (noatime) {
48091972cfcSAlan Somers 		build_iovec(&iov, &iovlen, "noatime",
48191972cfcSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
48291972cfcSAlan Somers 	}
483402b609cSAlan Somers 	if (noclusterr) {
484402b609cSAlan Somers 		build_iovec(&iov, &iovlen, "noclusterr",
485402b609cSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
486402b609cSAlan Somers 	}
487ed74f781SAlan Somers 	if (nointr) {
488ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "nointr",
489ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
490ed74f781SAlan Somers 	} else {
491ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "intr",
492ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
493ed74f781SAlan Somers 	}
4949821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
4959821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4969821f1d3SAlan Somers 			"Couldn't mount filesystem"));
4979821f1d3SAlan Somers 
4989821f1d3SAlan Somers 	// Setup default handler
4999821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
5009821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
5019821f1d3SAlan Somers 
5029821f1d3SAlan Somers 	init(flags);
5038b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
5048b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
5058b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
5068b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
5078b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5088b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
5099821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
5109821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5119821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
5129821f1d3SAlan Somers }
5139821f1d3SAlan Somers 
5149821f1d3SAlan Somers MockFS::~MockFS() {
5159821f1d3SAlan Somers 	kill_daemon();
5169821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
5179821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
5189821f1d3SAlan Somers 		m_daemon_id = NULL;
5199821f1d3SAlan Somers 	}
5208b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
5219821f1d3SAlan Somers 	rmdir("mountpoint");
5223429092cSAlan Somers 	if (m_kq >= 0)
5233429092cSAlan Somers 		close(m_kq);
5249821f1d3SAlan Somers }
5259821f1d3SAlan Somers 
5266c0c3620SAlan Somers void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
527bf507497SAlan Somers 	uint32_t inlen = in.header.len;
528bf507497SAlan Somers 	size_t fih = sizeof(in.header);
529bf507497SAlan Somers 	switch (in.header.opcode) {
530bf507497SAlan Somers 	case FUSE_LOOKUP:
531bf507497SAlan Somers 	case FUSE_RMDIR:
532bf507497SAlan Somers 	case FUSE_SYMLINK:
533bf507497SAlan Somers 	case FUSE_UNLINK:
5346c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request filename";
5356c0c3620SAlan Somers 		// No redundant information for checking buflen
536bf507497SAlan Somers 		break;
537bf507497SAlan Somers 	case FUSE_FORGET:
5386c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
5396c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
540bf507497SAlan Somers 		break;
541bf507497SAlan Somers 	case FUSE_GETATTR:
5426c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
5436c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
544bf507497SAlan Somers 		break;
545bf507497SAlan Somers 	case FUSE_SETATTR:
5466c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
5476c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
548bf507497SAlan Somers 		break;
549bf507497SAlan Somers 	case FUSE_READLINK:
5506c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
5516c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
552bf507497SAlan Somers 		break;
553bf507497SAlan Somers 	case FUSE_MKNOD:
554bf507497SAlan Somers 		{
555bf507497SAlan Somers 			size_t s;
556bf507497SAlan Somers 			if (m_kernel_minor_version >= 12)
557bf507497SAlan Somers 				s = sizeof(in.body.mknod);
558bf507497SAlan Somers 			else
559bf507497SAlan Somers 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
5606c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
5616c0c3620SAlan Somers 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
5626c0c3620SAlan Somers 			// No redundant information for checking buflen
563bf507497SAlan Somers 			break;
564bf507497SAlan Somers 		}
565bf507497SAlan Somers 	case FUSE_MKDIR:
5666c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
567bf507497SAlan Somers 			"Missing request body";
5686c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
569bf507497SAlan Somers 			"Missing request filename";
5706c0c3620SAlan Somers 		// No redundant information for checking buflen
571bf507497SAlan Somers 		break;
572bf507497SAlan Somers 	case FUSE_RENAME:
5736c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
574bf507497SAlan Somers 			"Missing request body";
5756c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
576bf507497SAlan Somers 			"Missing request filename";
5776c0c3620SAlan Somers 		// No redundant information for checking buflen
578bf507497SAlan Somers 		break;
579bf507497SAlan Somers 	case FUSE_LINK:
5806c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
581bf507497SAlan Somers 			"Missing request body";
5826c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
583bf507497SAlan Somers 			"Missing request filename";
5846c0c3620SAlan Somers 		// No redundant information for checking buflen
585bf507497SAlan Somers 		break;
586bf507497SAlan Somers 	case FUSE_OPEN:
5876c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
5886c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
589bf507497SAlan Somers 		break;
590bf507497SAlan Somers 	case FUSE_READ:
5916c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
5926c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
593bf507497SAlan Somers 		break;
594bf507497SAlan Somers 	case FUSE_WRITE:
595bf507497SAlan Somers 		{
596bf507497SAlan Somers 			size_t s;
597bf507497SAlan Somers 
598bf507497SAlan Somers 			if (m_kernel_minor_version >= 9)
599bf507497SAlan Somers 				s = sizeof(in.body.write);
600bf507497SAlan Somers 			else
601bf507497SAlan Somers 				s = FUSE_COMPAT_WRITE_IN_SIZE;
602bf507497SAlan Somers 			// I suppose a 0-byte write should be allowed
6036c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
6046c0c3620SAlan Somers 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
605bf507497SAlan Somers 			break;
606bf507497SAlan Somers 		}
607bf507497SAlan Somers 	case FUSE_DESTROY:
608bf507497SAlan Somers 	case FUSE_STATFS:
6096c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih);
6106c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
611bf507497SAlan Somers 		break;
612bf507497SAlan Somers 	case FUSE_RELEASE:
6136c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
6146c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
615bf507497SAlan Somers 		break;
616bf507497SAlan Somers 	case FUSE_FSYNC:
617bf507497SAlan Somers 	case FUSE_FSYNCDIR:
6186c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
6196c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
620bf507497SAlan Somers 		break;
621bf507497SAlan Somers 	case FUSE_SETXATTR:
6226c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
623bf507497SAlan Somers 			"Missing request body";
6246c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
625bf507497SAlan Somers 			"Missing request attribute name";
6266c0c3620SAlan Somers 		// No redundant information for checking buflen
627bf507497SAlan Somers 		break;
628bf507497SAlan Somers 	case FUSE_GETXATTR:
6296c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
630bf507497SAlan Somers 			"Missing request body";
6316c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
632bf507497SAlan Somers 			"Missing request attribute name";
6336c0c3620SAlan Somers 		// No redundant information for checking buflen
634bf507497SAlan Somers 		break;
635bf507497SAlan Somers 	case FUSE_LISTXATTR:
6366c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
6376c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
638bf507497SAlan Somers 		break;
639bf507497SAlan Somers 	case FUSE_REMOVEXATTR:
6406c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
6416c0c3620SAlan Somers 		// No redundant information for checking buflen
642bf507497SAlan Somers 		break;
643bf507497SAlan Somers 	case FUSE_FLUSH:
6446c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
6456c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
646bf507497SAlan Somers 		break;
647bf507497SAlan Somers 	case FUSE_INIT:
6486c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
6496c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
650bf507497SAlan Somers 		break;
651bf507497SAlan Somers 	case FUSE_OPENDIR:
6526c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
6536c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
654bf507497SAlan Somers 		break;
655bf507497SAlan Somers 	case FUSE_READDIR:
6566c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
6576c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
658bf507497SAlan Somers 		break;
659bf507497SAlan Somers 	case FUSE_RELEASEDIR:
6606c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
6616c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
662bf507497SAlan Somers 		break;
663bf507497SAlan Somers 	case FUSE_GETLK:
6646c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
6656c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
666bf507497SAlan Somers 		break;
667bf507497SAlan Somers 	case FUSE_SETLK:
668bf507497SAlan Somers 	case FUSE_SETLKW:
6696c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
6706c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
671bf507497SAlan Somers 		break;
672bf507497SAlan Somers 	case FUSE_ACCESS:
6736c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
6746c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
675bf507497SAlan Somers 		break;
676bf507497SAlan Somers 	case FUSE_CREATE:
6776c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
678bf507497SAlan Somers 			"Missing request body";
6796c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
680bf507497SAlan Somers 			"Missing request filename";
6816c0c3620SAlan Somers 		// No redundant information for checking buflen
682bf507497SAlan Somers 		break;
683bf507497SAlan Somers 	case FUSE_INTERRUPT:
6846c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
6856c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
686bf507497SAlan Somers 		break;
687bf507497SAlan Somers 	case FUSE_BMAP:
6886c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
6896c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
690bf507497SAlan Somers 		break;
69137df9d3bSAlan Somers 	case FUSE_LSEEK:
69237df9d3bSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
69337df9d3bSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
69437df9d3bSAlan Somers 		break;
69592bbfe1fSAlan Somers 	case FUSE_COPY_FILE_RANGE:
69692bbfe1fSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
69792bbfe1fSAlan Somers 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
69892bbfe1fSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
69992bbfe1fSAlan Somers 		break;
700bf507497SAlan Somers 	case FUSE_NOTIFY_REPLY:
701bf507497SAlan Somers 	case FUSE_BATCH_FORGET:
702bf507497SAlan Somers 	case FUSE_FALLOCATE:
703bf507497SAlan Somers 	case FUSE_IOCTL:
704bf507497SAlan Somers 	case FUSE_POLL:
705bf507497SAlan Somers 	case FUSE_READDIRPLUS:
706bf507497SAlan Somers 		FAIL() << "Unsupported opcode?";
707bf507497SAlan Somers 	default:
708bf507497SAlan Somers 		FAIL() << "Unknown opcode " << in.header.opcode;
709bf507497SAlan Somers 	}
7105403f2c1SAlan Somers 	/*
7115403f2c1SAlan Somers 	 * Check that the ticket's unique value is sequential.  Technically it
7125403f2c1SAlan Somers 	 * doesn't need to be sequential, merely unique.  But the current
7135403f2c1SAlan Somers 	 * fusefs driver _does_ make it sequential, and that's easy to check
7145403f2c1SAlan Somers 	 * for.
7155403f2c1SAlan Somers 	 */
7165403f2c1SAlan Somers 	if (in.header.unique != ++m_last_unique)
7175403f2c1SAlan Somers 		FAIL() << "Non-sequential unique value";
718bf507497SAlan Somers }
719bf507497SAlan Somers 
7209821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
7216c0c3620SAlan Somers 	ssize_t buflen;
7226c0c3620SAlan Somers 
72329edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
72429edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
7259821f1d3SAlan Somers 
7266c0c3620SAlan Somers 	read_request(*in, buflen);
72777b040c9SAlan Somers 	if (verbosity > 0)
72877b040c9SAlan Somers 		debug_request(*in, buflen);
7296c0c3620SAlan Somers 	audit_request(*in, buflen);
7309821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
7319821f1d3SAlan Somers 
7329821f1d3SAlan Somers 	out->header.unique = in->header.unique;
7339821f1d3SAlan Somers 	out->header.error = 0;
7349821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
73516bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
7369821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
7378eecd9ceSAlan Somers 	out->body.init.max_write = m_maxwrite;
7389821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
73987ff949aSAlan Somers 
74087ff949aSAlan Somers 	if (m_kernel_minor_version < 23) {
74187ff949aSAlan Somers 		SET_OUT_HEADER_LEN(*out, init_7_22);
74287ff949aSAlan Somers 	} else {
743fef46454SAlan Somers 		out->body.init.time_gran = m_time_gran;
74429edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out, init);
74587ff949aSAlan Somers 	}
74687ff949aSAlan Somers 
74729edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
7489821f1d3SAlan Somers }
7499821f1d3SAlan Somers 
7509821f1d3SAlan Somers void MockFS::kill_daemon() {
75181a619c4SAlan Somers 	m_quit = true;
7528b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
7539821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
7548b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
7558b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
7568b73a4c5SAlan Somers 	// during the unmount sequence.
7579821f1d3SAlan Somers 	close(m_fuse_fd);
7588b73a4c5SAlan Somers 	m_fuse_fd = -1;
7599821f1d3SAlan Somers }
7609821f1d3SAlan Somers 
7619821f1d3SAlan Somers void MockFS::loop() {
76229edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
7639821f1d3SAlan Somers 
76429edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
7659821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
76681a619c4SAlan Somers 	while (!m_quit) {
7676c0c3620SAlan Somers 		ssize_t buflen;
7686c0c3620SAlan Somers 
76929edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
7706c0c3620SAlan Somers 		read_request(*in, buflen);
77181a619c4SAlan Somers 		if (m_quit)
7729821f1d3SAlan Somers 			break;
7739821f1d3SAlan Somers 		if (verbosity > 0)
7746c0c3620SAlan Somers 			debug_request(*in, buflen);
7756c0c3620SAlan Somers 		audit_request(*in, buflen);
7769821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
77729edc611SAlan Somers 			process(*in, out);
7789821f1d3SAlan Somers 		} else {
7799821f1d3SAlan Somers 			/*
7809821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
7819821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
7829821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
7839821f1d3SAlan Somers 			 */
78499cf7bffSAlan Somers 			if (verbosity > 1)
78599cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
78699cf7bffSAlan Somers 					in->header.pid);
78729edc611SAlan Somers 			process_default(*in, out);
7889821f1d3SAlan Somers 		}
78929edc611SAlan Somers 		for (auto &it: out)
79029edc611SAlan Somers 			write_response(*it);
7919821f1d3SAlan Somers 		out.clear();
7929821f1d3SAlan Somers 	}
7939821f1d3SAlan Somers }
7949821f1d3SAlan Somers 
795c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
796c2d70d6eSAlan Somers {
797c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
798c2d70d6eSAlan Somers 
799c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
800c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
801c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
802c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
803c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
804c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
805c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
806c2d70d6eSAlan Somers 		namelen;
807c2d70d6eSAlan Somers 	debug_response(*out);
808c2d70d6eSAlan Somers 	write_response(*out);
809c2d70d6eSAlan Somers 	return 0;
810c2d70d6eSAlan Somers }
811c2d70d6eSAlan Somers 
812eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
813eae1ae13SAlan Somers {
814eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
815eae1ae13SAlan Somers 
816eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
817eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
818eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
819eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
820eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
821eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
822eae1ae13SAlan Somers 	debug_response(*out);
823eae1ae13SAlan Somers 	write_response(*out);
824eae1ae13SAlan Somers 	return 0;
825eae1ae13SAlan Somers }
826eae1ae13SAlan Somers 
8275a0b9a27SAlan Somers int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
8287cbb8e8aSAlan Somers {
8297cbb8e8aSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
8307cbb8e8aSAlan Somers 
8317cbb8e8aSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
8327cbb8e8aSAlan Somers 	out->header.error = FUSE_NOTIFY_STORE;
8337cbb8e8aSAlan Somers 	out->body.store.nodeid = ino;
8347cbb8e8aSAlan Somers 	out->body.store.offset = off;
8357cbb8e8aSAlan Somers 	out->body.store.size = size;
8367cbb8e8aSAlan Somers 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
8377cbb8e8aSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
8387cbb8e8aSAlan Somers 	debug_response(*out);
8397cbb8e8aSAlan Somers 	write_response(*out);
8407cbb8e8aSAlan Somers 	return 0;
8417cbb8e8aSAlan Somers }
8427cbb8e8aSAlan Somers 
8439821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
8449821f1d3SAlan Somers 	if (pid == m_pid) {
8459821f1d3SAlan Somers 		return (true);
84691ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
84791ff3a0dSAlan Somers 		return (true);
8489821f1d3SAlan Somers 	} else {
8499821f1d3SAlan Somers 		struct kinfo_proc *ki;
8509821f1d3SAlan Somers 		bool ok = false;
8519821f1d3SAlan Somers 
8529821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
8539821f1d3SAlan Somers 		if (ki == NULL)
8549821f1d3SAlan Somers 			return (false);
8559821f1d3SAlan Somers 		/*
8569821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
8579821f1d3SAlan Somers 		 * can use aio functions
8589821f1d3SAlan Somers 		 */
8599821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
8609821f1d3SAlan Somers 			ok = true;
8619821f1d3SAlan Somers 		free(ki);
8629821f1d3SAlan Somers 		return (ok);
8639821f1d3SAlan Somers 	}
8649821f1d3SAlan Somers }
8659821f1d3SAlan Somers 
86629edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
86729edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
8689821f1d3SAlan Somers {
86929edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
87029edc611SAlan Somers 	out0->header.unique = in.header.unique;
8719821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
8729821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
87329edc611SAlan Somers 	out.push_back(std::move(out0));
8749821f1d3SAlan Somers }
8759821f1d3SAlan Somers 
8766c0c3620SAlan Somers void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
87777fbe694SAlan Somers 	int nready = 0;
8783429092cSAlan Somers 	fd_set readfds;
8793429092cSAlan Somers 	pollfd fds[1];
8803429092cSAlan Somers 	struct kevent changes[1];
8813429092cSAlan Somers 	struct kevent events[1];
88277fbe694SAlan Somers 	struct timespec timeout_ts;
88377fbe694SAlan Somers 	struct timeval timeout_tv;
88477fbe694SAlan Somers 	const int timeout_ms = 999;
88577fbe694SAlan Somers 	int timeout_int, nfds;
886f44a4487SAlan Somers 	int fuse_fd;
8879821f1d3SAlan Somers 
8883429092cSAlan Somers 	switch (m_pm) {
8893429092cSAlan Somers 	case BLOCKING:
8903429092cSAlan Somers 		break;
8913429092cSAlan Somers 	case KQ:
89277fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
89377fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
89477fbe694SAlan Somers 		while (nready == 0) {
8957b8622faSAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
8967b8622faSAlan Somers 				EV_ADD | EV_ONESHOT, 0, 0, 0);
89777fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
89877fbe694SAlan Somers 				&timeout_ts);
8993429092cSAlan Somers 			if (m_quit)
9003429092cSAlan Somers 				return;
90177fbe694SAlan Somers 		}
9023429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9033429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
9043429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
9053429092cSAlan Somers 			FAIL() << strerror(events[0].data);
9063429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
9073429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
9080a7c63e0SAlan Somers 		m_nready = events[0].data;
9093429092cSAlan Somers 		break;
9103429092cSAlan Somers 	case POLL:
91177fbe694SAlan Somers 		timeout_int = timeout_ms;
9123429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
9133429092cSAlan Somers 		fds[0].events = POLLIN;
91477fbe694SAlan Somers 		while (nready == 0) {
91577fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
9163429092cSAlan Somers 			if (m_quit)
9173429092cSAlan Somers 				return;
91877fbe694SAlan Somers 		}
9193429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9203429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
9213429092cSAlan Somers 		break;
9223429092cSAlan Somers 	case SELECT:
923f44a4487SAlan Somers 		fuse_fd = m_fuse_fd;
924f44a4487SAlan Somers 		if (fuse_fd < 0)
925f44a4487SAlan Somers 			break;
92677fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
92777fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
928f44a4487SAlan Somers 		nfds = fuse_fd + 1;
92977fbe694SAlan Somers 		while (nready == 0) {
9303429092cSAlan Somers 			FD_ZERO(&readfds);
931f44a4487SAlan Somers 			FD_SET(fuse_fd, &readfds);
93277fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
93377fbe694SAlan Somers 				&timeout_tv);
9343429092cSAlan Somers 			if (m_quit)
9353429092cSAlan Somers 				return;
93677fbe694SAlan Somers 		}
9373429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
938f44a4487SAlan Somers 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
9393429092cSAlan Somers 		break;
9403429092cSAlan Somers 	default:
9413429092cSAlan Somers 		FAIL() << "not yet implemented";
9423429092cSAlan Somers 	}
94329edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
9443429092cSAlan Somers 
945b690d120SAlan Somers 	if (res < 0 && !m_quit) {
946b690d120SAlan Somers 		m_quit = true;
9478e765737SAlan Somers 		FAIL() << "read: " << strerror(errno);
948b690d120SAlan Somers 	}
94929edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
950bf507497SAlan Somers 	/*
951bf507497SAlan Somers 	 * Inconsistently, fuse_in_header.len is the size of the entire
952bf507497SAlan Somers 	 * request,including header, even though fuse_out_header.len excludes
953bf507497SAlan Somers 	 * the size of the header.
954bf507497SAlan Somers 	 */
95538a3e0bdSAlan Somers 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
9569821f1d3SAlan Somers }
9579821f1d3SAlan Somers 
95829edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
9593429092cSAlan Somers 	fd_set writefds;
9603429092cSAlan Somers 	pollfd fds[1];
9617b8622faSAlan Somers 	struct kevent changes[1];
9627b8622faSAlan Somers 	struct kevent events[1];
9633429092cSAlan Somers 	int nready, nfds;
9643429092cSAlan Somers 	ssize_t r;
9653429092cSAlan Somers 
9663429092cSAlan Somers 	switch (m_pm) {
9673429092cSAlan Somers 	case BLOCKING:
9687b8622faSAlan Somers 		break;
9697b8622faSAlan Somers 	case KQ:
9707b8622faSAlan Somers 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
9717b8622faSAlan Somers 			EV_ADD | EV_ONESHOT, 0, 0, 0);
9727b8622faSAlan Somers 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
9737b8622faSAlan Somers 			NULL);
9747b8622faSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9757b8622faSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
9767b8622faSAlan Somers 		if (events[0].flags & EV_ERROR)
9777b8622faSAlan Somers 			FAIL() << strerror(events[0].data);
9787b8622faSAlan Somers 		else if (events[0].flags & EV_EOF)
9797b8622faSAlan Somers 			FAIL() << strerror(events[0].fflags);
9807b8622faSAlan Somers 		m_nready = events[0].data;
9813429092cSAlan Somers 		break;
9823429092cSAlan Somers 	case POLL:
9833429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
9843429092cSAlan Somers 		fds[0].events = POLLOUT;
9853429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
9863429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9873429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
9883429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
9893429092cSAlan Somers 		break;
9903429092cSAlan Somers 	case SELECT:
9913429092cSAlan Somers 		FD_ZERO(&writefds);
9923429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
9933429092cSAlan Somers 		nfds = m_fuse_fd + 1;
9943429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
9953429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9963429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
9973429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
9983429092cSAlan Somers 		break;
9993429092cSAlan Somers 	default:
10003429092cSAlan Somers 		FAIL() << "not yet implemented";
10013429092cSAlan Somers 	}
100229edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
10033429092cSAlan Somers 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
10043429092cSAlan Somers }
10053429092cSAlan Somers 
10069821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
10079821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
10089821f1d3SAlan Somers 
10099821f1d3SAlan Somers 	mock_fs->loop();
10109821f1d3SAlan Somers 
10119821f1d3SAlan Somers 	return (NULL);
10129821f1d3SAlan Somers }
10139821f1d3SAlan Somers 
10149821f1d3SAlan Somers void MockFS::unmount() {
10159821f1d3SAlan Somers 	::unmount("mountpoint", 0);
10169821f1d3SAlan Somers }
1017