xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision f44a448709d3b77508fd59ee28201ae1666387c2)
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;
2939821f1d3SAlan Somers 		case FUSE_SETATTR:
2949821f1d3SAlan Somers 			if (verbosity <= 1) {
29529edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
2969821f1d3SAlan Somers 				break;
2979821f1d3SAlan Somers 			}
29829edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
29929edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
30029edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
30129edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
30229edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
30329edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
30429edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
30529edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
30629edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
307cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
30829edc611SAlan Somers 					in.body.setattr.atime,
30929edc611SAlan Somers 					in.body.setattr.atimensec);
31029edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
311cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
31229edc611SAlan Somers 					in.body.setattr.mtime,
31329edc611SAlan Somers 					in.body.setattr.mtimensec);
31429edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
31529edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
3169821f1d3SAlan Somers 			break;
317f067b609SAlan Somers 		case FUSE_SETLK:
318cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
319cc04566cSAlan Somers 				" type=%u pid=%u",
32029edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
32129edc611SAlan Somers 				in.body.setlk.lk.type,
32229edc611SAlan Somers 				in.body.setlk.lk.pid);
323f067b609SAlan Somers 			if (verbosity >= 2) {
324cc04566cSAlan Somers 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
32529edc611SAlan Somers 					in.body.setlk.lk.start,
32629edc611SAlan Somers 					in.body.setlk.lk.end);
327f067b609SAlan Somers 			}
328f067b609SAlan Somers 			break;
3299821f1d3SAlan Somers 		case FUSE_SETXATTR:
3309821f1d3SAlan Somers 			/*
3319821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
3329821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
3339821f1d3SAlan Somers 			 */
33429edc611SAlan Somers 			name = (const char*)in.body.bytes +
3359821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
33619ef317dSAlan Somers 			value = name + strlen(name) + 1;
33719ef317dSAlan Somers 			printf(" %s=%s", name, value);
3389821f1d3SAlan Somers 			break;
3399821f1d3SAlan Somers 		case FUSE_WRITE:
340cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
341d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
34229edc611SAlan Somers 				in.body.write.fh,
34329edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
34429edc611SAlan Somers 				in.body.write.write_flags);
345d4fd0c81SAlan Somers 			if (verbosity > 1)
346d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
3479821f1d3SAlan Somers 			break;
3489821f1d3SAlan Somers 		default:
3499821f1d3SAlan Somers 			break;
3509821f1d3SAlan Somers 	}
3519821f1d3SAlan Somers 	printf("\n");
3529821f1d3SAlan Somers }
3539821f1d3SAlan Somers 
354c2d70d6eSAlan Somers /*
355c2d70d6eSAlan Somers  * Debug a FUSE response.
356c2d70d6eSAlan Somers  *
357c2d70d6eSAlan Somers  * This is mostly useful for asynchronous notifications, which don't correspond
358c2d70d6eSAlan Somers  * to any request
359c2d70d6eSAlan Somers  */
360c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
361c2d70d6eSAlan Somers 	const char *name;
362c2d70d6eSAlan Somers 
363c2d70d6eSAlan Somers 	if (verbosity == 0)
364c2d70d6eSAlan Somers 		return;
365c2d70d6eSAlan Somers 
366c2d70d6eSAlan Somers 	switch (out.header.error) {
367c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
368c2d70d6eSAlan Somers 			name = (const char*)out.body.bytes +
369c2d70d6eSAlan Somers 				sizeof(fuse_notify_inval_entry_out);
370c2d70d6eSAlan Somers 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
371c2d70d6eSAlan Somers 				out.body.inval_entry.parent, name);
372c2d70d6eSAlan Somers 			break;
373eae1ae13SAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
374eae1ae13SAlan Somers 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
375eae1ae13SAlan Somers 				" len=%" PRIi64 "\n",
376eae1ae13SAlan Somers 				out.body.inval_inode.ino,
377eae1ae13SAlan Somers 				out.body.inval_inode.off,
378eae1ae13SAlan Somers 				out.body.inval_inode.len);
379eae1ae13SAlan Somers 			break;
3807cbb8e8aSAlan Somers 		case FUSE_NOTIFY_STORE:
3817cbb8e8aSAlan Somers 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
3827cbb8e8aSAlan Somers 				" size=%" PRIu32 "\n",
3837cbb8e8aSAlan Somers 				out.body.store.nodeid,
3847cbb8e8aSAlan Somers 				out.body.store.offset,
3857cbb8e8aSAlan Somers 				out.body.store.size);
3867cbb8e8aSAlan Somers 			break;
387c2d70d6eSAlan Somers 		default:
388c2d70d6eSAlan Somers 			break;
389c2d70d6eSAlan Somers 	}
390c2d70d6eSAlan Somers }
391c2d70d6eSAlan Somers 
39291ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
39316bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
394402b609cSAlan Somers 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
395ed74f781SAlan Somers 	bool noclusterr, unsigned time_gran, bool nointr)
3969821f1d3SAlan Somers {
3978b73a4c5SAlan Somers 	struct sigaction sa;
3989821f1d3SAlan Somers 	struct iovec *iov = NULL;
3999821f1d3SAlan Somers 	int iovlen = 0;
4009821f1d3SAlan Somers 	char fdstr[15];
40191ff3a0dSAlan Somers 	const bool trueval = true;
4029821f1d3SAlan Somers 
4039821f1d3SAlan Somers 	m_daemon_id = NULL;
40416bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
4059821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
406f928dbcbSAlan Somers 	m_maxwrite = MIN(max_write, max_max_write);
4070a7c63e0SAlan Somers 	m_nready = -1;
4083429092cSAlan Somers 	m_pm = pm;
409fef46454SAlan Somers 	m_time_gran = time_gran;
41081a619c4SAlan Somers 	m_quit = false;
4115403f2c1SAlan Somers 	m_last_unique = 0;
4123429092cSAlan Somers 	if (m_pm == KQ)
4133429092cSAlan Somers 		m_kq = kqueue();
4143429092cSAlan Somers 	else
4153429092cSAlan Somers 		m_kq = -1;
4169821f1d3SAlan Somers 
4179821f1d3SAlan Somers 	/*
4189821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
4199821f1d3SAlan Somers 	 * mkdtemp
4209821f1d3SAlan Somers 	 */
4219821f1d3SAlan Somers 	/*
4229821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
4239821f1d3SAlan Somers 	 * instead.
4249821f1d3SAlan Somers 	 */
42591ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
4269821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4279821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
4289821f1d3SAlan Somers 
4293429092cSAlan Somers 	switch (m_pm) {
4303429092cSAlan Somers 	case BLOCKING:
43191ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
4323429092cSAlan Somers 		break;
4333429092cSAlan Somers 	default:
4343429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
4353429092cSAlan Somers 		break;
4363429092cSAlan Somers 	}
4379821f1d3SAlan Somers 	if (m_fuse_fd < 0)
4389821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4399821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
4409821f1d3SAlan Somers 
4419821f1d3SAlan Somers 	m_pid = getpid();
44291ff3a0dSAlan Somers 	m_child_pid = -1;
4439821f1d3SAlan Somers 
4449821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
4459821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
4469821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
4479821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
4483429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
4499821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
45091ff3a0dSAlan Somers 	if (allow_other) {
45191ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
4529821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4539821f1d3SAlan Somers 	}
4549821f1d3SAlan Somers 	if (default_permissions) {
4559821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
4569821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
4579821f1d3SAlan Somers 	}
45891ff3a0dSAlan Somers 	if (push_symlinks_in) {
45991ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
46091ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
46191ff3a0dSAlan Somers 	}
462140bb492SAlan Somers 	if (ro) {
463140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
464140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
465140bb492SAlan Somers 	}
4668eecd9ceSAlan Somers 	if (async) {
4678eecd9ceSAlan Somers 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
4688eecd9ceSAlan Somers 			sizeof(bool));
4698eecd9ceSAlan Somers 	}
470402b609cSAlan Somers 	if (noclusterr) {
471402b609cSAlan Somers 		build_iovec(&iov, &iovlen, "noclusterr",
472402b609cSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
473402b609cSAlan Somers 	}
474ed74f781SAlan Somers 	if (nointr) {
475ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "nointr",
476ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
477ed74f781SAlan Somers 	} else {
478ed74f781SAlan Somers 		build_iovec(&iov, &iovlen, "intr",
479ed74f781SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
480ed74f781SAlan Somers 	}
4819821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
4829821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4839821f1d3SAlan Somers 			"Couldn't mount filesystem"));
4849821f1d3SAlan Somers 
4859821f1d3SAlan Somers 	// Setup default handler
4869821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
4879821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
4889821f1d3SAlan Somers 
4899821f1d3SAlan Somers 	init(flags);
4908b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
4918b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
4928b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
4938b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
4948b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4958b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
4969821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
4979821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4989821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
4999821f1d3SAlan Somers }
5009821f1d3SAlan Somers 
5019821f1d3SAlan Somers MockFS::~MockFS() {
5029821f1d3SAlan Somers 	kill_daemon();
5039821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
5049821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
5059821f1d3SAlan Somers 		m_daemon_id = NULL;
5069821f1d3SAlan Somers 	}
5078b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
5089821f1d3SAlan Somers 	rmdir("mountpoint");
5093429092cSAlan Somers 	if (m_kq >= 0)
5103429092cSAlan Somers 		close(m_kq);
5119821f1d3SAlan Somers }
5129821f1d3SAlan Somers 
5136c0c3620SAlan Somers void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
514bf507497SAlan Somers 	uint32_t inlen = in.header.len;
515bf507497SAlan Somers 	size_t fih = sizeof(in.header);
516bf507497SAlan Somers 	switch (in.header.opcode) {
517bf507497SAlan Somers 	case FUSE_LOOKUP:
518bf507497SAlan Somers 	case FUSE_RMDIR:
519bf507497SAlan Somers 	case FUSE_SYMLINK:
520bf507497SAlan Somers 	case FUSE_UNLINK:
5216c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request filename";
5226c0c3620SAlan Somers 		// No redundant information for checking buflen
523bf507497SAlan Somers 		break;
524bf507497SAlan Somers 	case FUSE_FORGET:
5256c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
5266c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
527bf507497SAlan Somers 		break;
528bf507497SAlan Somers 	case FUSE_GETATTR:
5296c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
5306c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
531bf507497SAlan Somers 		break;
532bf507497SAlan Somers 	case FUSE_SETATTR:
5336c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
5346c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
535bf507497SAlan Somers 		break;
536bf507497SAlan Somers 	case FUSE_READLINK:
5376c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
5386c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
539bf507497SAlan Somers 		break;
540bf507497SAlan Somers 	case FUSE_MKNOD:
541bf507497SAlan Somers 		{
542bf507497SAlan Somers 			size_t s;
543bf507497SAlan Somers 			if (m_kernel_minor_version >= 12)
544bf507497SAlan Somers 				s = sizeof(in.body.mknod);
545bf507497SAlan Somers 			else
546bf507497SAlan Somers 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
5476c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
5486c0c3620SAlan Somers 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
5496c0c3620SAlan Somers 			// No redundant information for checking buflen
550bf507497SAlan Somers 			break;
551bf507497SAlan Somers 		}
552bf507497SAlan Somers 	case FUSE_MKDIR:
5536c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
554bf507497SAlan Somers 			"Missing request body";
5556c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
556bf507497SAlan Somers 			"Missing request filename";
5576c0c3620SAlan Somers 		// No redundant information for checking buflen
558bf507497SAlan Somers 		break;
559bf507497SAlan Somers 	case FUSE_RENAME:
5606c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
561bf507497SAlan Somers 			"Missing request body";
5626c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
563bf507497SAlan Somers 			"Missing request filename";
5646c0c3620SAlan Somers 		// No redundant information for checking buflen
565bf507497SAlan Somers 		break;
566bf507497SAlan Somers 	case FUSE_LINK:
5676c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
568bf507497SAlan Somers 			"Missing request body";
5696c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
570bf507497SAlan Somers 			"Missing request filename";
5716c0c3620SAlan Somers 		// No redundant information for checking buflen
572bf507497SAlan Somers 		break;
573bf507497SAlan Somers 	case FUSE_OPEN:
5746c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
5756c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
576bf507497SAlan Somers 		break;
577bf507497SAlan Somers 	case FUSE_READ:
5786c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
5796c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
580bf507497SAlan Somers 		break;
581bf507497SAlan Somers 	case FUSE_WRITE:
582bf507497SAlan Somers 		{
583bf507497SAlan Somers 			size_t s;
584bf507497SAlan Somers 
585bf507497SAlan Somers 			if (m_kernel_minor_version >= 9)
586bf507497SAlan Somers 				s = sizeof(in.body.write);
587bf507497SAlan Somers 			else
588bf507497SAlan Somers 				s = FUSE_COMPAT_WRITE_IN_SIZE;
589bf507497SAlan Somers 			// I suppose a 0-byte write should be allowed
5906c0c3620SAlan Somers 			EXPECT_GE(inlen, fih + s) << "Missing request body";
5916c0c3620SAlan Somers 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
592bf507497SAlan Somers 			break;
593bf507497SAlan Somers 		}
594bf507497SAlan Somers 	case FUSE_DESTROY:
595bf507497SAlan Somers 	case FUSE_STATFS:
5966c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih);
5976c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
598bf507497SAlan Somers 		break;
599bf507497SAlan Somers 	case FUSE_RELEASE:
6006c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
6016c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
602bf507497SAlan Somers 		break;
603bf507497SAlan Somers 	case FUSE_FSYNC:
604bf507497SAlan Somers 	case FUSE_FSYNCDIR:
6056c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
6066c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
607bf507497SAlan Somers 		break;
608bf507497SAlan Somers 	case FUSE_SETXATTR:
6096c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
610bf507497SAlan Somers 			"Missing request body";
6116c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
612bf507497SAlan Somers 			"Missing request attribute name";
6136c0c3620SAlan Somers 		// No redundant information for checking buflen
614bf507497SAlan Somers 		break;
615bf507497SAlan Somers 	case FUSE_GETXATTR:
6166c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
617bf507497SAlan Somers 			"Missing request body";
6186c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
619bf507497SAlan Somers 			"Missing request attribute name";
6206c0c3620SAlan Somers 		// No redundant information for checking buflen
621bf507497SAlan Somers 		break;
622bf507497SAlan Somers 	case FUSE_LISTXATTR:
6236c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
6246c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
625bf507497SAlan Somers 		break;
626bf507497SAlan Somers 	case FUSE_REMOVEXATTR:
6276c0c3620SAlan Somers 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
6286c0c3620SAlan Somers 		// No redundant information for checking buflen
629bf507497SAlan Somers 		break;
630bf507497SAlan Somers 	case FUSE_FLUSH:
6316c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
6326c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
633bf507497SAlan Somers 		break;
634bf507497SAlan Somers 	case FUSE_INIT:
6356c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
6366c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
637bf507497SAlan Somers 		break;
638bf507497SAlan Somers 	case FUSE_OPENDIR:
6396c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
6406c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
641bf507497SAlan Somers 		break;
642bf507497SAlan Somers 	case FUSE_READDIR:
6436c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
6446c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
645bf507497SAlan Somers 		break;
646bf507497SAlan Somers 	case FUSE_RELEASEDIR:
6476c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
6486c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
649bf507497SAlan Somers 		break;
650bf507497SAlan Somers 	case FUSE_GETLK:
6516c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
6526c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
653bf507497SAlan Somers 		break;
654bf507497SAlan Somers 	case FUSE_SETLK:
655bf507497SAlan Somers 	case FUSE_SETLKW:
6566c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
6576c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
658bf507497SAlan Somers 		break;
659bf507497SAlan Somers 	case FUSE_ACCESS:
6606c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
6616c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
662bf507497SAlan Somers 		break;
663bf507497SAlan Somers 	case FUSE_CREATE:
6646c0c3620SAlan Somers 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
665bf507497SAlan Somers 			"Missing request body";
6666c0c3620SAlan Somers 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
667bf507497SAlan Somers 			"Missing request filename";
6686c0c3620SAlan Somers 		// No redundant information for checking buflen
669bf507497SAlan Somers 		break;
670bf507497SAlan Somers 	case FUSE_INTERRUPT:
6716c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
6726c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
673bf507497SAlan Somers 		break;
674bf507497SAlan Somers 	case FUSE_BMAP:
6756c0c3620SAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
6766c0c3620SAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
677bf507497SAlan Somers 		break;
67837df9d3bSAlan Somers 	case FUSE_LSEEK:
67937df9d3bSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
68037df9d3bSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
68137df9d3bSAlan Somers 		break;
68292bbfe1fSAlan Somers 	case FUSE_COPY_FILE_RANGE:
68392bbfe1fSAlan Somers 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
68492bbfe1fSAlan Somers 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
68592bbfe1fSAlan Somers 		EXPECT_EQ((size_t)buflen, inlen);
68692bbfe1fSAlan Somers 		break;
687bf507497SAlan Somers 	case FUSE_NOTIFY_REPLY:
688bf507497SAlan Somers 	case FUSE_BATCH_FORGET:
689bf507497SAlan Somers 	case FUSE_FALLOCATE:
690bf507497SAlan Somers 	case FUSE_IOCTL:
691bf507497SAlan Somers 	case FUSE_POLL:
692bf507497SAlan Somers 	case FUSE_READDIRPLUS:
693bf507497SAlan Somers 		FAIL() << "Unsupported opcode?";
694bf507497SAlan Somers 	default:
695bf507497SAlan Somers 		FAIL() << "Unknown opcode " << in.header.opcode;
696bf507497SAlan Somers 	}
6975403f2c1SAlan Somers 	/*
6985403f2c1SAlan Somers 	 * Check that the ticket's unique value is sequential.  Technically it
6995403f2c1SAlan Somers 	 * doesn't need to be sequential, merely unique.  But the current
7005403f2c1SAlan Somers 	 * fusefs driver _does_ make it sequential, and that's easy to check
7015403f2c1SAlan Somers 	 * for.
7025403f2c1SAlan Somers 	 */
7035403f2c1SAlan Somers 	if (in.header.unique != ++m_last_unique)
7045403f2c1SAlan Somers 		FAIL() << "Non-sequential unique value";
705bf507497SAlan Somers }
706bf507497SAlan Somers 
7079821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
7086c0c3620SAlan Somers 	ssize_t buflen;
7096c0c3620SAlan Somers 
71029edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
71129edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
7129821f1d3SAlan Somers 
7136c0c3620SAlan Somers 	read_request(*in, buflen);
71477b040c9SAlan Somers 	if (verbosity > 0)
71577b040c9SAlan Somers 		debug_request(*in, buflen);
7166c0c3620SAlan Somers 	audit_request(*in, buflen);
7179821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
7189821f1d3SAlan Somers 
7199821f1d3SAlan Somers 	out->header.unique = in->header.unique;
7209821f1d3SAlan Somers 	out->header.error = 0;
7219821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
72216bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
7239821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
7248eecd9ceSAlan Somers 	out->body.init.max_write = m_maxwrite;
7259821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
72687ff949aSAlan Somers 
72787ff949aSAlan Somers 	if (m_kernel_minor_version < 23) {
72887ff949aSAlan Somers 		SET_OUT_HEADER_LEN(*out, init_7_22);
72987ff949aSAlan Somers 	} else {
730fef46454SAlan Somers 		out->body.init.time_gran = m_time_gran;
73129edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out, init);
73287ff949aSAlan Somers 	}
73387ff949aSAlan Somers 
73429edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
7359821f1d3SAlan Somers }
7369821f1d3SAlan Somers 
7379821f1d3SAlan Somers void MockFS::kill_daemon() {
73881a619c4SAlan Somers 	m_quit = true;
7398b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
7409821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
7418b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
7428b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
7438b73a4c5SAlan Somers 	// during the unmount sequence.
7449821f1d3SAlan Somers 	close(m_fuse_fd);
7458b73a4c5SAlan Somers 	m_fuse_fd = -1;
7469821f1d3SAlan Somers }
7479821f1d3SAlan Somers 
7489821f1d3SAlan Somers void MockFS::loop() {
74929edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
7509821f1d3SAlan Somers 
75129edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
7529821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
75381a619c4SAlan Somers 	while (!m_quit) {
7546c0c3620SAlan Somers 		ssize_t buflen;
7556c0c3620SAlan Somers 
75629edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
7576c0c3620SAlan Somers 		read_request(*in, buflen);
75881a619c4SAlan Somers 		if (m_quit)
7599821f1d3SAlan Somers 			break;
7609821f1d3SAlan Somers 		if (verbosity > 0)
7616c0c3620SAlan Somers 			debug_request(*in, buflen);
7626c0c3620SAlan Somers 		audit_request(*in, buflen);
7639821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
76429edc611SAlan Somers 			process(*in, out);
7659821f1d3SAlan Somers 		} else {
7669821f1d3SAlan Somers 			/*
7679821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
7689821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
7699821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
7709821f1d3SAlan Somers 			 */
77199cf7bffSAlan Somers 			if (verbosity > 1)
77299cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
77399cf7bffSAlan Somers 					in->header.pid);
77429edc611SAlan Somers 			process_default(*in, out);
7759821f1d3SAlan Somers 		}
77629edc611SAlan Somers 		for (auto &it: out)
77729edc611SAlan Somers 			write_response(*it);
7789821f1d3SAlan Somers 		out.clear();
7799821f1d3SAlan Somers 	}
7809821f1d3SAlan Somers }
7819821f1d3SAlan Somers 
782c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
783c2d70d6eSAlan Somers {
784c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
785c2d70d6eSAlan Somers 
786c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
787c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
788c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
789c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
790c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
791c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
792c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
793c2d70d6eSAlan Somers 		namelen;
794c2d70d6eSAlan Somers 	debug_response(*out);
795c2d70d6eSAlan Somers 	write_response(*out);
796c2d70d6eSAlan Somers 	return 0;
797c2d70d6eSAlan Somers }
798c2d70d6eSAlan Somers 
799eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
800eae1ae13SAlan Somers {
801eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
802eae1ae13SAlan Somers 
803eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
804eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
805eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
806eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
807eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
808eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
809eae1ae13SAlan Somers 	debug_response(*out);
810eae1ae13SAlan Somers 	write_response(*out);
811eae1ae13SAlan Somers 	return 0;
812eae1ae13SAlan Somers }
813eae1ae13SAlan Somers 
8145a0b9a27SAlan Somers int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
8157cbb8e8aSAlan Somers {
8167cbb8e8aSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
8177cbb8e8aSAlan Somers 
8187cbb8e8aSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
8197cbb8e8aSAlan Somers 	out->header.error = FUSE_NOTIFY_STORE;
8207cbb8e8aSAlan Somers 	out->body.store.nodeid = ino;
8217cbb8e8aSAlan Somers 	out->body.store.offset = off;
8227cbb8e8aSAlan Somers 	out->body.store.size = size;
8237cbb8e8aSAlan Somers 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
8247cbb8e8aSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
8257cbb8e8aSAlan Somers 	debug_response(*out);
8267cbb8e8aSAlan Somers 	write_response(*out);
8277cbb8e8aSAlan Somers 	return 0;
8287cbb8e8aSAlan Somers }
8297cbb8e8aSAlan Somers 
8309821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
8319821f1d3SAlan Somers 	if (pid == m_pid) {
8329821f1d3SAlan Somers 		return (true);
83391ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
83491ff3a0dSAlan Somers 		return (true);
8359821f1d3SAlan Somers 	} else {
8369821f1d3SAlan Somers 		struct kinfo_proc *ki;
8379821f1d3SAlan Somers 		bool ok = false;
8389821f1d3SAlan Somers 
8399821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
8409821f1d3SAlan Somers 		if (ki == NULL)
8419821f1d3SAlan Somers 			return (false);
8429821f1d3SAlan Somers 		/*
8439821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
8449821f1d3SAlan Somers 		 * can use aio functions
8459821f1d3SAlan Somers 		 */
8469821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
8479821f1d3SAlan Somers 			ok = true;
8489821f1d3SAlan Somers 		free(ki);
8499821f1d3SAlan Somers 		return (ok);
8509821f1d3SAlan Somers 	}
8519821f1d3SAlan Somers }
8529821f1d3SAlan Somers 
85329edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
85429edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
8559821f1d3SAlan Somers {
85629edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
85729edc611SAlan Somers 	out0->header.unique = in.header.unique;
8589821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
8599821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
86029edc611SAlan Somers 	out.push_back(std::move(out0));
8619821f1d3SAlan Somers }
8629821f1d3SAlan Somers 
8636c0c3620SAlan Somers void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
86477fbe694SAlan Somers 	int nready = 0;
8653429092cSAlan Somers 	fd_set readfds;
8663429092cSAlan Somers 	pollfd fds[1];
8673429092cSAlan Somers 	struct kevent changes[1];
8683429092cSAlan Somers 	struct kevent events[1];
86977fbe694SAlan Somers 	struct timespec timeout_ts;
87077fbe694SAlan Somers 	struct timeval timeout_tv;
87177fbe694SAlan Somers 	const int timeout_ms = 999;
87277fbe694SAlan Somers 	int timeout_int, nfds;
873*f44a4487SAlan Somers 	int fuse_fd;
8749821f1d3SAlan Somers 
8753429092cSAlan Somers 	switch (m_pm) {
8763429092cSAlan Somers 	case BLOCKING:
8773429092cSAlan Somers 		break;
8783429092cSAlan Somers 	case KQ:
87977fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
88077fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
88177fbe694SAlan Somers 		while (nready == 0) {
8827b8622faSAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
8837b8622faSAlan Somers 				EV_ADD | EV_ONESHOT, 0, 0, 0);
88477fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
88577fbe694SAlan Somers 				&timeout_ts);
8863429092cSAlan Somers 			if (m_quit)
8873429092cSAlan Somers 				return;
88877fbe694SAlan Somers 		}
8893429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
8903429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
8913429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
8923429092cSAlan Somers 			FAIL() << strerror(events[0].data);
8933429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
8943429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
8950a7c63e0SAlan Somers 		m_nready = events[0].data;
8963429092cSAlan Somers 		break;
8973429092cSAlan Somers 	case POLL:
89877fbe694SAlan Somers 		timeout_int = timeout_ms;
8993429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
9003429092cSAlan Somers 		fds[0].events = POLLIN;
90177fbe694SAlan Somers 		while (nready == 0) {
90277fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
9033429092cSAlan Somers 			if (m_quit)
9043429092cSAlan Somers 				return;
90577fbe694SAlan Somers 		}
9063429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9073429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
9083429092cSAlan Somers 		break;
9093429092cSAlan Somers 	case SELECT:
910*f44a4487SAlan Somers 		fuse_fd = m_fuse_fd;
911*f44a4487SAlan Somers 		if (fuse_fd < 0)
912*f44a4487SAlan Somers 			break;
91377fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
91477fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
915*f44a4487SAlan Somers 		nfds = fuse_fd + 1;
91677fbe694SAlan Somers 		while (nready == 0) {
9173429092cSAlan Somers 			FD_ZERO(&readfds);
918*f44a4487SAlan Somers 			FD_SET(fuse_fd, &readfds);
91977fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
92077fbe694SAlan Somers 				&timeout_tv);
9213429092cSAlan Somers 			if (m_quit)
9223429092cSAlan Somers 				return;
92377fbe694SAlan Somers 		}
9243429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
925*f44a4487SAlan Somers 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
9263429092cSAlan Somers 		break;
9273429092cSAlan Somers 	default:
9283429092cSAlan Somers 		FAIL() << "not yet implemented";
9293429092cSAlan Somers 	}
93029edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
9313429092cSAlan Somers 
932b690d120SAlan Somers 	if (res < 0 && !m_quit) {
933b690d120SAlan Somers 		m_quit = true;
9348e765737SAlan Somers 		FAIL() << "read: " << strerror(errno);
935b690d120SAlan Somers 	}
93629edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
937bf507497SAlan Somers 	/*
938bf507497SAlan Somers 	 * Inconsistently, fuse_in_header.len is the size of the entire
939bf507497SAlan Somers 	 * request,including header, even though fuse_out_header.len excludes
940bf507497SAlan Somers 	 * the size of the header.
941bf507497SAlan Somers 	 */
94238a3e0bdSAlan Somers 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
9439821f1d3SAlan Somers }
9449821f1d3SAlan Somers 
94529edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
9463429092cSAlan Somers 	fd_set writefds;
9473429092cSAlan Somers 	pollfd fds[1];
9487b8622faSAlan Somers 	struct kevent changes[1];
9497b8622faSAlan Somers 	struct kevent events[1];
9503429092cSAlan Somers 	int nready, nfds;
9513429092cSAlan Somers 	ssize_t r;
9523429092cSAlan Somers 
9533429092cSAlan Somers 	switch (m_pm) {
9543429092cSAlan Somers 	case BLOCKING:
9557b8622faSAlan Somers 		break;
9567b8622faSAlan Somers 	case KQ:
9577b8622faSAlan Somers 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
9587b8622faSAlan Somers 			EV_ADD | EV_ONESHOT, 0, 0, 0);
9597b8622faSAlan Somers 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
9607b8622faSAlan Somers 			NULL);
9617b8622faSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9627b8622faSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
9637b8622faSAlan Somers 		if (events[0].flags & EV_ERROR)
9647b8622faSAlan Somers 			FAIL() << strerror(events[0].data);
9657b8622faSAlan Somers 		else if (events[0].flags & EV_EOF)
9667b8622faSAlan Somers 			FAIL() << strerror(events[0].fflags);
9677b8622faSAlan Somers 		m_nready = events[0].data;
9683429092cSAlan Somers 		break;
9693429092cSAlan Somers 	case POLL:
9703429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
9713429092cSAlan Somers 		fds[0].events = POLLOUT;
9723429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
9733429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9743429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
9753429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
9763429092cSAlan Somers 		break;
9773429092cSAlan Somers 	case SELECT:
9783429092cSAlan Somers 		FD_ZERO(&writefds);
9793429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
9803429092cSAlan Somers 		nfds = m_fuse_fd + 1;
9813429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
9823429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
9833429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
9843429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
9853429092cSAlan Somers 		break;
9863429092cSAlan Somers 	default:
9873429092cSAlan Somers 		FAIL() << "not yet implemented";
9883429092cSAlan Somers 	}
98929edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
9903429092cSAlan Somers 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
9913429092cSAlan Somers }
9923429092cSAlan Somers 
9939821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
9949821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
9959821f1d3SAlan Somers 
9969821f1d3SAlan Somers 	mock_fs->loop();
9979821f1d3SAlan Somers 
9989821f1d3SAlan Somers 	return (NULL);
9999821f1d3SAlan Somers }
10009821f1d3SAlan Somers 
10019821f1d3SAlan Somers void MockFS::unmount() {
10029821f1d3SAlan Somers 	::unmount("mountpoint", 0);
10039821f1d3SAlan Somers }
1004