xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 2f6362484c0e1250ef6a76cccdbe4a95587e850c)
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;
210398c88c7SAlan Somers 		case FUSE_FALLOCATE:
211398c88c7SAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
212398c88c7SAlan Somers 				" length=%" PRIx64 " mode=%#x",
213398c88c7SAlan Somers 				in.body.fallocate.fh,
214398c88c7SAlan Somers 				in.body.fallocate.offset,
215398c88c7SAlan Somers 				in.body.fallocate.length,
216398c88c7SAlan Somers 				in.body.fallocate.mode);
217398c88c7SAlan Somers 			break;
2189821f1d3SAlan Somers 		case FUSE_FLUSH:
219cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
22029edc611SAlan Somers 				in.body.flush.fh,
22129edc611SAlan Somers 				in.body.flush.lock_owner);
2229821f1d3SAlan Somers 			break;
2239821f1d3SAlan Somers 		case FUSE_FORGET:
22429edc611SAlan Somers 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
2259821f1d3SAlan Somers 			break;
2269821f1d3SAlan Somers 		case FUSE_FSYNC:
22729edc611SAlan Somers 			printf(" flags=%#x", in.body.fsync.fsync_flags);
2289821f1d3SAlan Somers 			break;
2299821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
23029edc611SAlan Somers 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
2319821f1d3SAlan Somers 			break;
232723c7768SAlan Somers 		case FUSE_INTERRUPT:
23329edc611SAlan Somers 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
234723c7768SAlan Somers 			break;
235002e54b0SAlan Somers 		case FUSE_LINK:
23629edc611SAlan Somers 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
237002e54b0SAlan Somers 			break;
2385e633330SAlan Somers 		case FUSE_LISTXATTR:
2395e633330SAlan Somers 			printf(" size=%" PRIu32, in.body.listxattr.size);
2405e633330SAlan Somers 			break;
2419821f1d3SAlan Somers 		case FUSE_LOOKUP:
24229edc611SAlan Somers 			printf(" %s", in.body.lookup);
2439821f1d3SAlan Somers 			break;
24437df9d3bSAlan Somers 		case FUSE_LSEEK:
24537df9d3bSAlan Somers 			switch (in.body.lseek.whence) {
24637df9d3bSAlan Somers 			case SEEK_HOLE:
2471d010cd3SCy Schubert 				printf(" SEEK_HOLE offset=%jd",
24837df9d3bSAlan Somers 				    in.body.lseek.offset);
24937df9d3bSAlan Somers 				break;
25037df9d3bSAlan Somers 			case SEEK_DATA:
2511d010cd3SCy Schubert 				printf(" SEEK_DATA offset=%jd",
25237df9d3bSAlan Somers 				    in.body.lseek.offset);
25337df9d3bSAlan Somers 				break;
25437df9d3bSAlan Somers 			default:
2551d010cd3SCy Schubert 				printf(" whence=%u offset=%jd",
25637df9d3bSAlan Somers 				    in.body.lseek.whence, in.body.lseek.offset);
25737df9d3bSAlan Somers 				break;
25837df9d3bSAlan Somers 			}
25937df9d3bSAlan Somers 			break;
26099cf7bffSAlan Somers 		case FUSE_MKDIR:
26129edc611SAlan Somers 			name = (const char*)in.body.bytes +
26299cf7bffSAlan Somers 				sizeof(fuse_mkdir_in);
263a4856c96SAlan Somers 			printf(" name=%s mode=%#o umask=%#o", name,
264a4856c96SAlan Somers 				in.body.mkdir.mode, in.body.mkdir.umask);
26599cf7bffSAlan Somers 			break;
266bf4d7084SAlan Somers 		case FUSE_MKNOD:
267a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
268a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
269a4856c96SAlan Somers 					sizeof(fuse_mknod_in);
270a4856c96SAlan Somers 			else
271a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
272a4856c96SAlan Somers 					FUSE_COMPAT_MKNOD_IN_SIZE;
273a4856c96SAlan Somers 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
274a4856c96SAlan Somers 				in.body.mknod.mode, in.body.mknod.rdev,
275a4856c96SAlan Somers 				in.body.mknod.umask, name);
276bf4d7084SAlan Somers 			break;
2779821f1d3SAlan Somers 		case FUSE_OPEN:
278a4856c96SAlan Somers 			printf(" flags=%#x", in.body.open.flags);
2799821f1d3SAlan Somers 			break;
2809821f1d3SAlan Somers 		case FUSE_OPENDIR:
281a4856c96SAlan Somers 			printf(" flags=%#x", in.body.opendir.flags);
2829821f1d3SAlan Somers 			break;
2839821f1d3SAlan Somers 		case FUSE_READ:
284cc04566cSAlan Somers 			printf(" offset=%" PRIu64 " size=%u",
28529edc611SAlan Somers 				in.body.read.offset,
28629edc611SAlan Somers 				in.body.read.size);
287d4fd0c81SAlan Somers 			if (verbosity > 1)
288d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.read.flags);
2899821f1d3SAlan Somers 			break;
2909821f1d3SAlan Somers 		case FUSE_READDIR:
291cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
29229edc611SAlan Somers 				in.body.readdir.fh, in.body.readdir.offset,
29329edc611SAlan Somers 				in.body.readdir.size);
2949821f1d3SAlan Somers 			break;
2959821f1d3SAlan Somers 		case FUSE_RELEASE:
296cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
29729edc611SAlan Somers 				in.body.release.fh,
29829edc611SAlan Somers 				in.body.release.flags,
29929edc611SAlan Somers 				in.body.release.lock_owner);
3009821f1d3SAlan Somers 			break;
301c2d342c5SAlan Somers 		case FUSE_RENAME:
302c2d342c5SAlan Somers 			{
303c2d342c5SAlan Somers 				const char *src = (const char*)in.body.bytes +
304c2d342c5SAlan Somers 					sizeof(fuse_rename_in);
305c2d342c5SAlan Somers 				const char *dst = src + strlen(src) + 1;
306c2d342c5SAlan Somers 				printf(" src=%s newdir=%" PRIu64 " dst=%s",
307c2d342c5SAlan Somers 					src, in.body.rename.newdir, dst);
308c2d342c5SAlan Somers 			}
309c2d342c5SAlan Somers 			break;
3109821f1d3SAlan Somers 		case FUSE_SETATTR:
3119821f1d3SAlan Somers 			if (verbosity <= 1) {
31229edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
3139821f1d3SAlan Somers 				break;
3149821f1d3SAlan Somers 			}
31529edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
31629edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
31729edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
31829edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
31929edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
32029edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
32129edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
32229edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
32329edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
324cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
32529edc611SAlan Somers 					in.body.setattr.atime,
32629edc611SAlan Somers 					in.body.setattr.atimensec);
32729edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
328cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
32929edc611SAlan Somers 					in.body.setattr.mtime,
33029edc611SAlan Somers 					in.body.setattr.mtimensec);
33129edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
33229edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
3339821f1d3SAlan Somers 			break;
334f067b609SAlan Somers 		case FUSE_SETLK:
335cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
336cc04566cSAlan Somers 				" type=%u pid=%u",
33729edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
33829edc611SAlan Somers 				in.body.setlk.lk.type,
33929edc611SAlan Somers 				in.body.setlk.lk.pid);
340f067b609SAlan Somers 			if (verbosity >= 2) {
341cc04566cSAlan Somers 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
34229edc611SAlan Somers 					in.body.setlk.lk.start,
34329edc611SAlan Somers 					in.body.setlk.lk.end);
344f067b609SAlan Somers 			}
345f067b609SAlan Somers 			break;
3469821f1d3SAlan Somers 		case FUSE_SETXATTR:
3479821f1d3SAlan Somers 			/*
3489821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
3499821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
3509821f1d3SAlan Somers 			 */
35129edc611SAlan Somers 			name = (const char*)in.body.bytes +
3529821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
35319ef317dSAlan Somers 			value = name + strlen(name) + 1;
35419ef317dSAlan Somers 			printf(" %s=%s", name, value);
3559821f1d3SAlan Somers 			break;
3569821f1d3SAlan Somers 		case FUSE_WRITE:
357cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
358d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
35929edc611SAlan Somers 				in.body.write.fh,
36029edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
36129edc611SAlan Somers 				in.body.write.write_flags);
362d4fd0c81SAlan Somers 			if (verbosity > 1)
363d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
3649821f1d3SAlan Somers 			break;
3659821f1d3SAlan Somers 		default:
3669821f1d3SAlan Somers 			break;
3679821f1d3SAlan Somers 	}
3689821f1d3SAlan Somers 	printf("\n");
3699821f1d3SAlan Somers }
3709821f1d3SAlan Somers 
371c2d70d6eSAlan Somers /*
372c2d70d6eSAlan Somers  * Debug a FUSE response.
373c2d70d6eSAlan Somers  *
374c2d70d6eSAlan Somers  * This is mostly useful for asynchronous notifications, which don't correspond
375c2d70d6eSAlan Somers  * to any request
376c2d70d6eSAlan Somers  */
377c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
378c2d70d6eSAlan Somers 	const char *name;
379c2d70d6eSAlan Somers 
380c2d70d6eSAlan Somers 	if (verbosity == 0)
381c2d70d6eSAlan Somers 		return;
382c2d70d6eSAlan Somers 
383c2d70d6eSAlan Somers 	switch (out.header.error) {
384c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
385c2d70d6eSAlan Somers 			name = (const char*)out.body.bytes +
386c2d70d6eSAlan Somers 				sizeof(fuse_notify_inval_entry_out);
387c2d70d6eSAlan Somers 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
388c2d70d6eSAlan Somers 				out.body.inval_entry.parent, name);
389c2d70d6eSAlan Somers 			break;
390eae1ae13SAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
391eae1ae13SAlan Somers 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
392eae1ae13SAlan Somers 				" len=%" PRIi64 "\n",
393eae1ae13SAlan Somers 				out.body.inval_inode.ino,
394eae1ae13SAlan Somers 				out.body.inval_inode.off,
395eae1ae13SAlan Somers 				out.body.inval_inode.len);
396eae1ae13SAlan Somers 			break;
3977cbb8e8aSAlan Somers 		case FUSE_NOTIFY_STORE:
3987cbb8e8aSAlan Somers 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
3997cbb8e8aSAlan Somers 				" size=%" PRIu32 "\n",
4007cbb8e8aSAlan Somers 				out.body.store.nodeid,
4017cbb8e8aSAlan Somers 				out.body.store.offset,
4027cbb8e8aSAlan Somers 				out.body.store.size);
4037cbb8e8aSAlan Somers 			break;
404c2d70d6eSAlan Somers 		default:
405c2d70d6eSAlan Somers 			break;
406c2d70d6eSAlan Somers 	}
407c2d70d6eSAlan Somers }
408c2d70d6eSAlan Somers 
40991ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
41016bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
411402b609cSAlan Somers 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
412616eaa66SAlan Somers 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
413*2f636248SAlan Somers 	const char *fsname, const char *subtype)
4149821f1d3SAlan Somers {
4158b73a4c5SAlan Somers 	struct sigaction sa;
4169821f1d3SAlan Somers 	struct iovec *iov = NULL;
4179821f1d3SAlan Somers 	int iovlen = 0;
4189821f1d3SAlan Somers 	char fdstr[15];
41991ff3a0dSAlan Somers 	const bool trueval = true;
4209821f1d3SAlan Somers 
4219821f1d3SAlan Somers 	m_daemon_id = NULL;
422155ac516SAlan Somers 	m_expected_write_errno = 0;
42316bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
4249821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
425f928dbcbSAlan Somers 	m_maxwrite = MIN(max_write, max_max_write);
4260a7c63e0SAlan Somers 	m_nready = -1;
4273429092cSAlan Somers 	m_pm = pm;
428fef46454SAlan Somers 	m_time_gran = time_gran;
42981a619c4SAlan Somers 	m_quit = false;
4305403f2c1SAlan Somers 	m_last_unique = 0;
4313429092cSAlan Somers 	if (m_pm == KQ)
4323429092cSAlan Somers 		m_kq = kqueue();
4333429092cSAlan Somers 	else
4343429092cSAlan Somers 		m_kq = -1;
4359821f1d3SAlan Somers 
4369821f1d3SAlan Somers 	/*
4379821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
4389821f1d3SAlan Somers 	 * mkdtemp
4399821f1d3SAlan Somers 	 */
4409821f1d3SAlan Somers 	/*
4419821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
4429821f1d3SAlan Somers 	 * instead.
4439821f1d3SAlan Somers 	 */
44491ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
4459821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4469821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
4479821f1d3SAlan Somers 
4483429092cSAlan Somers 	switch (m_pm) {
4493429092cSAlan Somers 	case BLOCKING:
45091ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
4513429092cSAlan Somers 		break;
4523429092cSAlan Somers 	default:
4533429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
4543429092cSAlan Somers 		break;
4553429092cSAlan Somers 	}
4569821f1d3SAlan Somers 	if (m_fuse_fd < 0)
4579821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4589821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
4599821f1d3SAlan Somers 
4609821f1d3SAlan Somers 	m_pid = getpid();
46191ff3a0dSAlan Somers 	m_child_pid = -1;
4629821f1d3SAlan Somers 
4639821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4649821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
4659821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
4669821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4673429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
4689821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
46991ff3a0dSAlan Somers 	if (allow_other) {
47091ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
4719821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4729821f1d3SAlan Somers 	}
4739821f1d3SAlan Somers 	if (default_permissions) {
4749821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
4759821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4769821f1d3SAlan Somers 	}
47791ff3a0dSAlan Somers 	if (push_symlinks_in) {
47891ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
47991ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
48091ff3a0dSAlan Somers 	}
481140bb492SAlan Somers 	if (ro) {
482140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
483140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
484140bb492SAlan Somers 	}
4858eecd9ceSAlan Somers 	if (async) {
4868eecd9ceSAlan Somers 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4878eecd9ceSAlan Somers 			sizeof(bool));
4888eecd9ceSAlan Somers 	}
48991972cfcSAlan Somers 	if (noatime) {
49091972cfcSAlan Somers 		build_iovec(&iov, &iovlen, "noatime",
49191972cfcSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
49291972cfcSAlan Somers 	}
493402b609cSAlan Somers 	if (noclusterr) {
494402b609cSAlan Somers 		build_iovec(&iov, &iovlen, "noclusterr",
495402b609cSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
496402b609cSAlan Somers 	}
497ed74f781SAlan Somers 	if (nointr) {
498ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "nointr",
499ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
500ed74f781SAlan Somers 	} else {
501ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "intr",
502ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
503ed74f781SAlan Somers 	}
504*2f636248SAlan Somers 	if (*fsname) {
505*2f636248SAlan Somers 		build_iovec(&iov, &iovlen, "fsname=",
506*2f636248SAlan Somers 			__DECONST(void*, fsname), -1);
507*2f636248SAlan Somers 	}
508616eaa66SAlan Somers 	if (*subtype) {
509616eaa66SAlan Somers 		build_iovec(&iov, &iovlen, "subtype=",
510616eaa66SAlan Somers 			__DECONST(void*, subtype), -1);
511616eaa66SAlan Somers 	}
5129821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
5139821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5149821f1d3SAlan Somers 			"Couldn't mount filesystem"));
5159821f1d3SAlan Somers 
5169821f1d3SAlan Somers 	// Setup default handler
5179821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
5189821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
5199821f1d3SAlan Somers 
5209821f1d3SAlan Somers 	init(flags);
5218b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
5228b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
5238b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
5248b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
5258b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5268b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
5279821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
5289821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
5299821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
5309821f1d3SAlan Somers }
5319821f1d3SAlan Somers 
5329821f1d3SAlan Somers MockFS::~MockFS() {
5339821f1d3SAlan Somers 	kill_daemon();
5349821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
5359821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
5369821f1d3SAlan Somers 		m_daemon_id = NULL;
5379821f1d3SAlan Somers 	}
5388b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
5399821f1d3SAlan Somers 	rmdir("mountpoint");
5403429092cSAlan Somers 	if (m_kq >= 0)
5413429092cSAlan Somers 		close(m_kq);
5429821f1d3SAlan Somers }
5439821f1d3SAlan Somers 
5446c0c3620SAlan Somers void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
545bf507497SAlan Somers 	uint32_t inlen = in.header.len;
546bf507497SAlan Somers 	size_t fih = sizeof(in.header);
547bf507497SAlan Somers 	switch (in.header.opcode) {
548bf507497SAlan Somers 	case FUSE_LOOKUP:
549bf507497SAlan Somers 	case FUSE_RMDIR:
550bf507497SAlan Somers 	case FUSE_SYMLINK:
551bf507497SAlan Somers 	case FUSE_UNLINK:
5526c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request filename";
5536c0c3620SAlan Somers 		// No redundant information for checking buflen
554bf507497SAlan Somers 		break;
555bf507497SAlan Somers 	case FUSE_FORGET:
5566c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
5576c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
558bf507497SAlan Somers 		break;
559bf507497SAlan Somers 	case FUSE_GETATTR:
5606c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
5616c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
562bf507497SAlan Somers 		break;
563bf507497SAlan Somers 	case FUSE_SETATTR:
5646c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
5656c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
566bf507497SAlan Somers 		break;
567bf507497SAlan Somers 	case FUSE_READLINK:
5686c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
5696c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
570bf507497SAlan Somers 		break;
571bf507497SAlan Somers 	case FUSE_MKNOD:
572bf507497SAlan Somers 		{
573bf507497SAlan Somers 			size_t s;
574bf507497SAlan Somers 			if (m_kernel_minor_version >= 12)
575bf507497SAlan Somers 				s = sizeof(in.body.mknod);
576bf507497SAlan Somers 			else
577bf507497SAlan Somers 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
5786c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
5796c0c3620SAlan Somers 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
5806c0c3620SAlan Somers 			// No redundant information for checking buflen
581bf507497SAlan Somers 			break;
582bf507497SAlan Somers 		}
583bf507497SAlan Somers 	case FUSE_MKDIR:
5846c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
585bf507497SAlan Somers 			"Missing request body";
5866c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
587bf507497SAlan Somers 			"Missing request filename";
5886c0c3620SAlan Somers 		// No redundant information for checking buflen
589bf507497SAlan Somers 		break;
590bf507497SAlan Somers 	case FUSE_RENAME:
5916c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
592bf507497SAlan Somers 			"Missing request body";
5936c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
594bf507497SAlan Somers 			"Missing request filename";
5956c0c3620SAlan Somers 		// No redundant information for checking buflen
596bf507497SAlan Somers 		break;
597bf507497SAlan Somers 	case FUSE_LINK:
5986c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
599bf507497SAlan Somers 			"Missing request body";
6006c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
601bf507497SAlan Somers 			"Missing request filename";
6026c0c3620SAlan Somers 		// No redundant information for checking buflen
603bf507497SAlan Somers 		break;
604bf507497SAlan Somers 	case FUSE_OPEN:
6056c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
6066c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
607bf507497SAlan Somers 		break;
608bf507497SAlan Somers 	case FUSE_READ:
6096c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
6106c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
611bf507497SAlan Somers 		break;
612bf507497SAlan Somers 	case FUSE_WRITE:
613bf507497SAlan Somers 		{
614bf507497SAlan Somers 			size_t s;
615bf507497SAlan Somers 
616bf507497SAlan Somers 			if (m_kernel_minor_version >= 9)
617bf507497SAlan Somers 				s = sizeof(in.body.write);
618bf507497SAlan Somers 			else
619bf507497SAlan Somers 				s = FUSE_COMPAT_WRITE_IN_SIZE;
620bf507497SAlan Somers 			// I suppose a 0-byte write should be allowed
6216c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
6226c0c3620SAlan Somers 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
623bf507497SAlan Somers 			break;
624bf507497SAlan Somers 		}
625bf507497SAlan Somers 	case FUSE_DESTROY:
626bf507497SAlan Somers 	case FUSE_STATFS:
6276c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih);
6286c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
629bf507497SAlan Somers 		break;
630bf507497SAlan Somers 	case FUSE_RELEASE:
6316c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
6326c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
633bf507497SAlan Somers 		break;
634bf507497SAlan Somers 	case FUSE_FSYNC:
635bf507497SAlan Somers 	case FUSE_FSYNCDIR:
6366c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
6376c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
638bf507497SAlan Somers 		break;
639bf507497SAlan Somers 	case FUSE_SETXATTR:
6406c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
641bf507497SAlan Somers 			"Missing request body";
6426c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
643bf507497SAlan Somers 			"Missing request attribute name";
6446c0c3620SAlan Somers 		// No redundant information for checking buflen
645bf507497SAlan Somers 		break;
646bf507497SAlan Somers 	case FUSE_GETXATTR:
6476c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
648bf507497SAlan Somers 			"Missing request body";
6496c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
650bf507497SAlan Somers 			"Missing request attribute name";
6516c0c3620SAlan Somers 		// No redundant information for checking buflen
652bf507497SAlan Somers 		break;
653bf507497SAlan Somers 	case FUSE_LISTXATTR:
6546c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
6556c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
656bf507497SAlan Somers 		break;
657bf507497SAlan Somers 	case FUSE_REMOVEXATTR:
6586c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
6596c0c3620SAlan Somers 		// No redundant information for checking buflen
660bf507497SAlan Somers 		break;
661bf507497SAlan Somers 	case FUSE_FLUSH:
6626c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
6636c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
664bf507497SAlan Somers 		break;
665bf507497SAlan Somers 	case FUSE_INIT:
6666c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
6676c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
668bf507497SAlan Somers 		break;
669bf507497SAlan Somers 	case FUSE_OPENDIR:
6706c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
6716c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
672bf507497SAlan Somers 		break;
673bf507497SAlan Somers 	case FUSE_READDIR:
6746c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
6756c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
676bf507497SAlan Somers 		break;
677bf507497SAlan Somers 	case FUSE_RELEASEDIR:
6786c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
6796c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
680bf507497SAlan Somers 		break;
681bf507497SAlan Somers 	case FUSE_GETLK:
6826c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
6836c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
684bf507497SAlan Somers 		break;
685bf507497SAlan Somers 	case FUSE_SETLK:
686bf507497SAlan Somers 	case FUSE_SETLKW:
6876c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
6886c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
689bf507497SAlan Somers 		break;
690bf507497SAlan Somers 	case FUSE_ACCESS:
6916c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
6926c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
693bf507497SAlan Somers 		break;
694bf507497SAlan Somers 	case FUSE_CREATE:
6956c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
696bf507497SAlan Somers 			"Missing request body";
6976c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
698bf507497SAlan Somers 			"Missing request filename";
6996c0c3620SAlan Somers 		// No redundant information for checking buflen
700bf507497SAlan Somers 		break;
701bf507497SAlan Somers 	case FUSE_INTERRUPT:
7026c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
7036c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
704bf507497SAlan Somers 		break;
705398c88c7SAlan Somers 	case FUSE_FALLOCATE:
706398c88c7SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
707398c88c7SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
708398c88c7SAlan Somers 		break;
709bf507497SAlan Somers 	case FUSE_BMAP:
7106c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
7116c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
712bf507497SAlan Somers 		break;
71337df9d3bSAlan Somers 	case FUSE_LSEEK:
71437df9d3bSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
71537df9d3bSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
71637df9d3bSAlan Somers 		break;
71792bbfe1fSAlan Somers 	case FUSE_COPY_FILE_RANGE:
71892bbfe1fSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
71992bbfe1fSAlan Somers 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
72092bbfe1fSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
72192bbfe1fSAlan Somers 		break;
722bf507497SAlan Somers 	case FUSE_NOTIFY_REPLY:
723bf507497SAlan Somers 	case FUSE_BATCH_FORGET:
724bf507497SAlan Somers 	case FUSE_IOCTL:
725bf507497SAlan Somers 	case FUSE_POLL:
726bf507497SAlan Somers 	case FUSE_READDIRPLUS:
727bf507497SAlan Somers 		FAIL() << "Unsupported opcode?";
728bf507497SAlan Somers 	default:
729bf507497SAlan Somers 		FAIL() << "Unknown opcode " << in.header.opcode;
730bf507497SAlan Somers 	}
7315403f2c1SAlan Somers 	/*
7325403f2c1SAlan Somers 	 * Check that the ticket's unique value is sequential.  Technically it
7335403f2c1SAlan Somers 	 * doesn't need to be sequential, merely unique.  But the current
7345403f2c1SAlan Somers 	 * fusefs driver _does_ make it sequential, and that's easy to check
7355403f2c1SAlan Somers 	 * for.
7365403f2c1SAlan Somers 	 */
7375403f2c1SAlan Somers 	if (in.header.unique != ++m_last_unique)
7385403f2c1SAlan Somers 		FAIL() << "Non-sequential unique value";
739bf507497SAlan Somers }
740bf507497SAlan Somers 
7419821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
7426c0c3620SAlan Somers 	ssize_t buflen;
7436c0c3620SAlan Somers 
74429edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
74529edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
7469821f1d3SAlan Somers 
7476c0c3620SAlan Somers 	read_request(*in, buflen);
74877b040c9SAlan Somers 	if (verbosity > 0)
74977b040c9SAlan Somers 		debug_request(*in, buflen);
7506c0c3620SAlan Somers 	audit_request(*in, buflen);
7519821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
7529821f1d3SAlan Somers 
7539821f1d3SAlan Somers 	out->header.unique = in->header.unique;
7549821f1d3SAlan Somers 	out->header.error = 0;
7559821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
75616bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
7579821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
7588eecd9ceSAlan Somers 	out->body.init.max_write = m_maxwrite;
7599821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
76087ff949aSAlan Somers 
76187ff949aSAlan Somers 	if (m_kernel_minor_version < 23) {
76287ff949aSAlan Somers 		SET_OUT_HEADER_LEN(*out, init_7_22);
76387ff949aSAlan Somers 	} else {
764fef46454SAlan Somers 		out->body.init.time_gran = m_time_gran;
76529edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out, init);
76687ff949aSAlan Somers 	}
76787ff949aSAlan Somers 
76829edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
7699821f1d3SAlan Somers }
7709821f1d3SAlan Somers 
7719821f1d3SAlan Somers void MockFS::kill_daemon() {
77281a619c4SAlan Somers 	m_quit = true;
7738b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
7749821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
7758b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
7768b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
7778b73a4c5SAlan Somers 	// during the unmount sequence.
7789821f1d3SAlan Somers 	close(m_fuse_fd);
7798b73a4c5SAlan Somers 	m_fuse_fd = -1;
7809821f1d3SAlan Somers }
7819821f1d3SAlan Somers 
7829821f1d3SAlan Somers void MockFS::loop() {
78329edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
7849821f1d3SAlan Somers 
78529edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
7869821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
78781a619c4SAlan Somers 	while (!m_quit) {
7886c0c3620SAlan Somers 		ssize_t buflen;
7896c0c3620SAlan Somers 
79029edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
7916c0c3620SAlan Somers 		read_request(*in, buflen);
792155ac516SAlan Somers 		m_expected_write_errno = 0;
79381a619c4SAlan Somers 		if (m_quit)
7949821f1d3SAlan Somers 			break;
7959821f1d3SAlan Somers 		if (verbosity > 0)
7966c0c3620SAlan Somers 			debug_request(*in, buflen);
7976c0c3620SAlan Somers 		audit_request(*in, buflen);
7989821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
79929edc611SAlan Somers 			process(*in, out);
8009821f1d3SAlan Somers 		} else {
8019821f1d3SAlan Somers 			/*
8029821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
8039821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
8049821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
8059821f1d3SAlan Somers 			 */
80699cf7bffSAlan Somers 			if (verbosity > 1)
80799cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
80899cf7bffSAlan Somers 					in->header.pid);
80929edc611SAlan Somers 			process_default(*in, out);
8109821f1d3SAlan Somers 		}
81129edc611SAlan Somers 		for (auto &it: out)
81229edc611SAlan Somers 			write_response(*it);
8139821f1d3SAlan Somers 		out.clear();
8149821f1d3SAlan Somers 	}
8159821f1d3SAlan Somers }
8169821f1d3SAlan Somers 
817c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
818c2d70d6eSAlan Somers {
819c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
820c2d70d6eSAlan Somers 
821c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
822c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
823c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
824c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
825c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
826c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
827c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
828c2d70d6eSAlan Somers 		namelen;
829c2d70d6eSAlan Somers 	debug_response(*out);
830c2d70d6eSAlan Somers 	write_response(*out);
831c2d70d6eSAlan Somers 	return 0;
832c2d70d6eSAlan Somers }
833c2d70d6eSAlan Somers 
834eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
835eae1ae13SAlan Somers {
836eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
837eae1ae13SAlan Somers 
838eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
839eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
840eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
841eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
842eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
843eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
844eae1ae13SAlan Somers 	debug_response(*out);
845eae1ae13SAlan Somers 	write_response(*out);
846eae1ae13SAlan Somers 	return 0;
847eae1ae13SAlan Somers }
848eae1ae13SAlan Somers 
8495a0b9a27SAlan Somers int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
8507cbb8e8aSAlan Somers {
8517cbb8e8aSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
8527cbb8e8aSAlan Somers 
8537cbb8e8aSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
8547cbb8e8aSAlan Somers 	out->header.error = FUSE_NOTIFY_STORE;
8557cbb8e8aSAlan Somers 	out->body.store.nodeid = ino;
8567cbb8e8aSAlan Somers 	out->body.store.offset = off;
8577cbb8e8aSAlan Somers 	out->body.store.size = size;
8587cbb8e8aSAlan Somers 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
8597cbb8e8aSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
8607cbb8e8aSAlan Somers 	debug_response(*out);
8617cbb8e8aSAlan Somers 	write_response(*out);
8627cbb8e8aSAlan Somers 	return 0;
8637cbb8e8aSAlan Somers }
8647cbb8e8aSAlan Somers 
8659821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
8669821f1d3SAlan Somers 	if (pid == m_pid) {
8679821f1d3SAlan Somers 		return (true);
86891ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
86991ff3a0dSAlan Somers 		return (true);
8709821f1d3SAlan Somers 	} else {
8719821f1d3SAlan Somers 		struct kinfo_proc *ki;
8729821f1d3SAlan Somers 		bool ok = false;
8739821f1d3SAlan Somers 
8749821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
8759821f1d3SAlan Somers 		if (ki == NULL)
8769821f1d3SAlan Somers 			return (false);
8779821f1d3SAlan Somers 		/*
8789821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
8799821f1d3SAlan Somers 		 * can use aio functions
8809821f1d3SAlan Somers 		 */
8819821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
8829821f1d3SAlan Somers 			ok = true;
8839821f1d3SAlan Somers 		free(ki);
8849821f1d3SAlan Somers 		return (ok);
8859821f1d3SAlan Somers 	}
8869821f1d3SAlan Somers }
8879821f1d3SAlan Somers 
88829edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
88929edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
8909821f1d3SAlan Somers {
89129edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
89229edc611SAlan Somers 	out0->header.unique = in.header.unique;
8939821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
8949821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
89529edc611SAlan Somers 	out.push_back(std::move(out0));
8969821f1d3SAlan Somers }
8979821f1d3SAlan Somers 
8986c0c3620SAlan Somers void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
89977fbe694SAlan Somers 	int nready = 0;
9003429092cSAlan Somers 	fd_set readfds;
9013429092cSAlan Somers 	pollfd fds[1];
9023429092cSAlan Somers 	struct kevent changes[1];
9033429092cSAlan Somers 	struct kevent events[1];
90477fbe694SAlan Somers 	struct timespec timeout_ts;
90577fbe694SAlan Somers 	struct timeval timeout_tv;
90677fbe694SAlan Somers 	const int timeout_ms = 999;
90777fbe694SAlan Somers 	int timeout_int, nfds;
908f44a4487SAlan Somers 	int fuse_fd;
9099821f1d3SAlan Somers 
9103429092cSAlan Somers 	switch (m_pm) {
9113429092cSAlan Somers 	case BLOCKING:
9123429092cSAlan Somers 		break;
9133429092cSAlan Somers 	case KQ:
91477fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
91577fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
91677fbe694SAlan Somers 		while (nready == 0) {
9177b8622faSAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
9187b8622faSAlan Somers 				EV_ADD | EV_ONESHOT, 0, 0, 0);
91977fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
92077fbe694SAlan Somers 				&timeout_ts);
9213429092cSAlan Somers 			if (m_quit)
9223429092cSAlan Somers 				return;
92377fbe694SAlan Somers 		}
9243429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9253429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
9263429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
9273429092cSAlan Somers 			FAIL() << strerror(events[0].data);
9283429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
9293429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
9300a7c63e0SAlan Somers 		m_nready = events[0].data;
9313429092cSAlan Somers 		break;
9323429092cSAlan Somers 	case POLL:
93377fbe694SAlan Somers 		timeout_int = timeout_ms;
9343429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
9353429092cSAlan Somers 		fds[0].events = POLLIN;
93677fbe694SAlan Somers 		while (nready == 0) {
93777fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
9383429092cSAlan Somers 			if (m_quit)
9393429092cSAlan Somers 				return;
94077fbe694SAlan Somers 		}
9413429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9423429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
9433429092cSAlan Somers 		break;
9443429092cSAlan Somers 	case SELECT:
945f44a4487SAlan Somers 		fuse_fd = m_fuse_fd;
946f44a4487SAlan Somers 		if (fuse_fd < 0)
947f44a4487SAlan Somers 			break;
94877fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
94977fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
950f44a4487SAlan Somers 		nfds = fuse_fd + 1;
95177fbe694SAlan Somers 		while (nready == 0) {
9523429092cSAlan Somers 			FD_ZERO(&readfds);
953f44a4487SAlan Somers 			FD_SET(fuse_fd, &readfds);
95477fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
95577fbe694SAlan Somers 				&timeout_tv);
9563429092cSAlan Somers 			if (m_quit)
9573429092cSAlan Somers 				return;
95877fbe694SAlan Somers 		}
9593429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
960f44a4487SAlan Somers 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
9613429092cSAlan Somers 		break;
9623429092cSAlan Somers 	default:
9633429092cSAlan Somers 		FAIL() << "not yet implemented";
9643429092cSAlan Somers 	}
96529edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
9663429092cSAlan Somers 
967b690d120SAlan Somers 	if (res < 0 && !m_quit) {
968b690d120SAlan Somers 		m_quit = true;
9698e765737SAlan Somers 		FAIL() << "read: " << strerror(errno);
970b690d120SAlan Somers 	}
97129edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
972bf507497SAlan Somers 	/*
973bf507497SAlan Somers 	 * Inconsistently, fuse_in_header.len is the size of the entire
974bf507497SAlan Somers 	 * request,including header, even though fuse_out_header.len excludes
975bf507497SAlan Somers 	 * the size of the header.
976bf507497SAlan Somers 	 */
97738a3e0bdSAlan Somers 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
9789821f1d3SAlan Somers }
9799821f1d3SAlan Somers 
98029edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
9813429092cSAlan Somers 	fd_set writefds;
9823429092cSAlan Somers 	pollfd fds[1];
9837b8622faSAlan Somers 	struct kevent changes[1];
9847b8622faSAlan Somers 	struct kevent events[1];
9853429092cSAlan Somers 	int nready, nfds;
9863429092cSAlan Somers 	ssize_t r;
9873429092cSAlan Somers 
9883429092cSAlan Somers 	switch (m_pm) {
9893429092cSAlan Somers 	case BLOCKING:
9907b8622faSAlan Somers 		break;
9917b8622faSAlan Somers 	case KQ:
9927b8622faSAlan Somers 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
9937b8622faSAlan Somers 			EV_ADD | EV_ONESHOT, 0, 0, 0);
9947b8622faSAlan Somers 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
9957b8622faSAlan Somers 			NULL);
9967b8622faSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9977b8622faSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
9987b8622faSAlan Somers 		if (events[0].flags & EV_ERROR)
9997b8622faSAlan Somers 			FAIL() << strerror(events[0].data);
10007b8622faSAlan Somers 		else if (events[0].flags & EV_EOF)
10017b8622faSAlan Somers 			FAIL() << strerror(events[0].fflags);
10027b8622faSAlan Somers 		m_nready = events[0].data;
10033429092cSAlan Somers 		break;
10043429092cSAlan Somers 	case POLL:
10053429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
10063429092cSAlan Somers 		fds[0].events = POLLOUT;
10073429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
10083429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
10093429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
10103429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
10113429092cSAlan Somers 		break;
10123429092cSAlan Somers 	case SELECT:
10133429092cSAlan Somers 		FD_ZERO(&writefds);
10143429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
10153429092cSAlan Somers 		nfds = m_fuse_fd + 1;
10163429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
10173429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
10183429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
10193429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
10203429092cSAlan Somers 		break;
10213429092cSAlan Somers 	default:
10223429092cSAlan Somers 		FAIL() << "not yet implemented";
10233429092cSAlan Somers 	}
102429edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
1025155ac516SAlan Somers 	if (m_expected_write_errno) {
1026155ac516SAlan Somers 		ASSERT_EQ(-1, r);
1027155ac516SAlan Somers 		ASSERT_EQ(m_expected_write_errno, errno) << strerror(errno);
1028155ac516SAlan Somers 	} else {
10293429092cSAlan Somers 		ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
10303429092cSAlan Somers 	}
1031155ac516SAlan Somers }
10323429092cSAlan Somers 
10339821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
10349821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
10359821f1d3SAlan Somers 
10369821f1d3SAlan Somers 	mock_fs->loop();
10379821f1d3SAlan Somers 
10389821f1d3SAlan Somers 	return (NULL);
10399821f1d3SAlan Somers }
10409821f1d3SAlan Somers 
10419821f1d3SAlan Somers void MockFS::unmount() {
10429821f1d3SAlan Somers 	::unmount("mountpoint", 0);
10439821f1d3SAlan Somers }
1044