xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision b690d120d9d52bc53142e615c244441fdba66e77)
19821f1d3SAlan Somers /*-
29821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
39821f1d3SAlan Somers  *
49821f1d3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers  *
69821f1d3SAlan Somers  * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers  * from the FreeBSD Foundation.
89821f1d3SAlan Somers  *
99821f1d3SAlan Somers  * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers  * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers  * are met:
129821f1d3SAlan Somers  * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers  *    documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers  *
189821f1d3SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers  * SUCH DAMAGE.
299821f1d3SAlan Somers  */
309821f1d3SAlan Somers 
319821f1d3SAlan Somers extern "C" {
329821f1d3SAlan Somers #include <sys/param.h>
339821f1d3SAlan Somers 
349821f1d3SAlan Somers #include <sys/mount.h>
353429092cSAlan Somers #include <sys/select.h>
369821f1d3SAlan Somers #include <sys/stat.h>
379821f1d3SAlan Somers #include <sys/uio.h>
389821f1d3SAlan Somers #include <sys/user.h>
399821f1d3SAlan Somers 
409821f1d3SAlan Somers #include <fcntl.h>
419821f1d3SAlan Somers #include <libutil.h>
423429092cSAlan Somers #include <poll.h>
439821f1d3SAlan Somers #include <pthread.h>
449821f1d3SAlan Somers #include <signal.h>
459821f1d3SAlan Somers #include <stdlib.h>
469821f1d3SAlan Somers #include <unistd.h>
479821f1d3SAlan Somers 
489821f1d3SAlan Somers #include "mntopts.h"	// for build_iovec
499821f1d3SAlan Somers }
509821f1d3SAlan Somers 
51cc04566cSAlan Somers #include <cinttypes>
52cc04566cSAlan Somers 
539821f1d3SAlan Somers #include <gtest/gtest.h>
549821f1d3SAlan Somers 
559821f1d3SAlan Somers #include "mockfs.hh"
569821f1d3SAlan Somers 
579821f1d3SAlan Somers using namespace testing;
589821f1d3SAlan Somers 
599821f1d3SAlan Somers int verbosity = 0;
609821f1d3SAlan Somers 
619821f1d3SAlan Somers const char* opcode2opname(uint32_t opcode)
629821f1d3SAlan Somers {
639821f1d3SAlan Somers 	const int NUM_OPS = 39;
649821f1d3SAlan Somers 	const char* table[NUM_OPS] = {
659821f1d3SAlan Somers 		"Unknown (opcode 0)",
669821f1d3SAlan Somers 		"LOOKUP",
679821f1d3SAlan Somers 		"FORGET",
689821f1d3SAlan Somers 		"GETATTR",
699821f1d3SAlan Somers 		"SETATTR",
709821f1d3SAlan Somers 		"READLINK",
719821f1d3SAlan Somers 		"SYMLINK",
729821f1d3SAlan Somers 		"Unknown (opcode 7)",
739821f1d3SAlan Somers 		"MKNOD",
749821f1d3SAlan Somers 		"MKDIR",
759821f1d3SAlan Somers 		"UNLINK",
769821f1d3SAlan Somers 		"RMDIR",
779821f1d3SAlan Somers 		"RENAME",
789821f1d3SAlan Somers 		"LINK",
799821f1d3SAlan Somers 		"OPEN",
809821f1d3SAlan Somers 		"READ",
819821f1d3SAlan Somers 		"WRITE",
829821f1d3SAlan Somers 		"STATFS",
839821f1d3SAlan Somers 		"RELEASE",
849821f1d3SAlan Somers 		"Unknown (opcode 19)",
859821f1d3SAlan Somers 		"FSYNC",
869821f1d3SAlan Somers 		"SETXATTR",
879821f1d3SAlan Somers 		"GETXATTR",
889821f1d3SAlan Somers 		"LISTXATTR",
899821f1d3SAlan Somers 		"REMOVEXATTR",
909821f1d3SAlan Somers 		"FLUSH",
919821f1d3SAlan Somers 		"INIT",
929821f1d3SAlan Somers 		"OPENDIR",
939821f1d3SAlan Somers 		"READDIR",
949821f1d3SAlan Somers 		"RELEASEDIR",
959821f1d3SAlan Somers 		"FSYNCDIR",
969821f1d3SAlan Somers 		"GETLK",
979821f1d3SAlan Somers 		"SETLK",
989821f1d3SAlan Somers 		"SETLKW",
999821f1d3SAlan Somers 		"ACCESS",
1009821f1d3SAlan Somers 		"CREATE",
1019821f1d3SAlan Somers 		"INTERRUPT",
1029821f1d3SAlan Somers 		"BMAP",
1039821f1d3SAlan Somers 		"DESTROY"
1049821f1d3SAlan Somers 	};
1059821f1d3SAlan Somers 	if (opcode >= NUM_OPS)
1069821f1d3SAlan Somers 		return ("Unknown (opcode > max)");
1079821f1d3SAlan Somers 	else
1089821f1d3SAlan Somers 		return (table[opcode]);
1099821f1d3SAlan Somers }
1109821f1d3SAlan Somers 
1119821f1d3SAlan Somers ProcessMockerT
1129821f1d3SAlan Somers ReturnErrno(int error)
1139821f1d3SAlan Somers {
1149821f1d3SAlan Somers 	return([=](auto in, auto &out) {
11529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
11629edc611SAlan Somers 		out0->header.unique = in.header.unique;
1179821f1d3SAlan Somers 		out0->header.error = -error;
1189821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
11929edc611SAlan Somers 		out.push_back(std::move(out0));
1209821f1d3SAlan Somers 	});
1219821f1d3SAlan Somers }
1229821f1d3SAlan Somers 
1239821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
1249821f1d3SAlan Somers ProcessMockerT
1259821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
1269821f1d3SAlan Somers {
1279821f1d3SAlan Somers 	return([=](auto in, auto &out) {
1289821f1d3SAlan Somers 		/* nodeid means ENOENT and cache it */
12929edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
1309821f1d3SAlan Somers 		out0->body.entry.nodeid = 0;
13129edc611SAlan Somers 		out0->header.unique = in.header.unique;
1329821f1d3SAlan Somers 		out0->header.error = 0;
1339821f1d3SAlan Somers 		out0->body.entry.entry_valid = entry_valid->tv_sec;
1349821f1d3SAlan Somers 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
13529edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
13629edc611SAlan Somers 		out.push_back(std::move(out0));
1379821f1d3SAlan Somers 	});
1389821f1d3SAlan Somers }
1399821f1d3SAlan Somers 
1409821f1d3SAlan Somers ProcessMockerT
14129edc611SAlan Somers ReturnImmediate(std::function<void(const mockfs_buf_in& in,
14229edc611SAlan Somers 				   struct mockfs_buf_out &out)> f)
1439821f1d3SAlan Somers {
14429edc611SAlan Somers 	return([=](auto& in, auto &out) {
14529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
14629edc611SAlan Somers 		out0->header.unique = in.header.unique;
14729edc611SAlan Somers 		f(in, *out0);
14829edc611SAlan Somers 		out.push_back(std::move(out0));
1499821f1d3SAlan Somers 	});
1509821f1d3SAlan Somers }
1519821f1d3SAlan Somers 
1529821f1d3SAlan Somers void sigint_handler(int __unused sig) {
1538b73a4c5SAlan Somers 	// Don't do anything except interrupt the daemon's read(2) call
1549821f1d3SAlan Somers }
1559821f1d3SAlan Somers 
156c2d70d6eSAlan Somers void MockFS::debug_request(const mockfs_buf_in &in)
1579821f1d3SAlan Somers {
15829edc611SAlan Somers 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
15929edc611SAlan Somers 		in.header.nodeid);
1609821f1d3SAlan Somers 	if (verbosity > 1) {
161cc04566cSAlan Somers 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u",
16229edc611SAlan Somers 			in.header.uid, in.header.gid, in.header.pid,
16329edc611SAlan Somers 			in.header.unique, in.header.len);
1649821f1d3SAlan Somers 	}
16529edc611SAlan Somers 	switch (in.header.opcode) {
16619ef317dSAlan Somers 		const char *name, *value;
16719ef317dSAlan Somers 
168caf5f57dSAlan Somers 		case FUSE_ACCESS:
16929edc611SAlan Somers 			printf(" mask=%#x", in.body.access.mask);
170caf5f57dSAlan Somers 			break;
17119ef317dSAlan Somers 		case FUSE_CREATE:
172a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
173a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
174a4856c96SAlan Somers 					sizeof(fuse_create_in);
175a4856c96SAlan Somers 			else
17629edc611SAlan Somers 				name = (const char*)in.body.bytes +
17719ef317dSAlan Somers 					sizeof(fuse_open_in);
17819ef317dSAlan Somers 			printf(" flags=%#x name=%s",
17929edc611SAlan Somers 				in.body.open.flags, name);
18019ef317dSAlan Somers 			break;
1819821f1d3SAlan Somers 		case FUSE_FLUSH:
182cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
18329edc611SAlan Somers 				in.body.flush.fh,
18429edc611SAlan Somers 				in.body.flush.lock_owner);
1859821f1d3SAlan Somers 			break;
1869821f1d3SAlan Somers 		case FUSE_FORGET:
18729edc611SAlan Somers 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
1889821f1d3SAlan Somers 			break;
1899821f1d3SAlan Somers 		case FUSE_FSYNC:
19029edc611SAlan Somers 			printf(" flags=%#x", in.body.fsync.fsync_flags);
1919821f1d3SAlan Somers 			break;
1929821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
19329edc611SAlan Somers 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
1949821f1d3SAlan Somers 			break;
195723c7768SAlan Somers 		case FUSE_INTERRUPT:
19629edc611SAlan Somers 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
197723c7768SAlan Somers 			break;
198002e54b0SAlan Somers 		case FUSE_LINK:
19929edc611SAlan Somers 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
200002e54b0SAlan Somers 			break;
2019821f1d3SAlan Somers 		case FUSE_LOOKUP:
20229edc611SAlan Somers 			printf(" %s", in.body.lookup);
2039821f1d3SAlan Somers 			break;
20499cf7bffSAlan Somers 		case FUSE_MKDIR:
20529edc611SAlan Somers 			name = (const char*)in.body.bytes +
20699cf7bffSAlan Somers 				sizeof(fuse_mkdir_in);
207a4856c96SAlan Somers 			printf(" name=%s mode=%#o umask=%#o", name,
208a4856c96SAlan Somers 				in.body.mkdir.mode, in.body.mkdir.umask);
20999cf7bffSAlan Somers 			break;
210bf4d7084SAlan Somers 		case FUSE_MKNOD:
211a4856c96SAlan Somers 			if (m_kernel_minor_version >= 12)
212a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
213a4856c96SAlan Somers 					sizeof(fuse_mknod_in);
214a4856c96SAlan Somers 			else
215a4856c96SAlan Somers 				name = (const char*)in.body.bytes +
216a4856c96SAlan Somers 					FUSE_COMPAT_MKNOD_IN_SIZE;
217a4856c96SAlan Somers 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
218a4856c96SAlan Somers 				in.body.mknod.mode, in.body.mknod.rdev,
219a4856c96SAlan Somers 				in.body.mknod.umask, name);
220bf4d7084SAlan Somers 			break;
2219821f1d3SAlan Somers 		case FUSE_OPEN:
222a4856c96SAlan Somers 			printf(" flags=%#x", in.body.open.flags);
2239821f1d3SAlan Somers 			break;
2249821f1d3SAlan Somers 		case FUSE_OPENDIR:
225a4856c96SAlan Somers 			printf(" flags=%#x", in.body.opendir.flags);
2269821f1d3SAlan Somers 			break;
2279821f1d3SAlan Somers 		case FUSE_READ:
228cc04566cSAlan Somers 			printf(" offset=%" PRIu64 " size=%u",
22929edc611SAlan Somers 				in.body.read.offset,
23029edc611SAlan Somers 				in.body.read.size);
231d4fd0c81SAlan Somers 			if (verbosity > 1)
232d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.read.flags);
2339821f1d3SAlan Somers 			break;
2349821f1d3SAlan Somers 		case FUSE_READDIR:
235cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
23629edc611SAlan Somers 				in.body.readdir.fh, in.body.readdir.offset,
23729edc611SAlan Somers 				in.body.readdir.size);
2389821f1d3SAlan Somers 			break;
2399821f1d3SAlan Somers 		case FUSE_RELEASE:
240cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
24129edc611SAlan Somers 				in.body.release.fh,
24229edc611SAlan Somers 				in.body.release.flags,
24329edc611SAlan Somers 				in.body.release.lock_owner);
2449821f1d3SAlan Somers 			break;
2459821f1d3SAlan Somers 		case FUSE_SETATTR:
2469821f1d3SAlan Somers 			if (verbosity <= 1) {
24729edc611SAlan Somers 				printf(" valid=%#x", in.body.setattr.valid);
2489821f1d3SAlan Somers 				break;
2499821f1d3SAlan Somers 			}
25029edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MODE)
25129edc611SAlan Somers 				printf(" mode=%#o", in.body.setattr.mode);
25229edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_UID)
25329edc611SAlan Somers 				printf(" uid=%u", in.body.setattr.uid);
25429edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_GID)
25529edc611SAlan Somers 				printf(" gid=%u", in.body.setattr.gid);
25629edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_SIZE)
25729edc611SAlan Somers 				printf(" size=%" PRIu64, in.body.setattr.size);
25829edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_ATIME)
259cc04566cSAlan Somers 				printf(" atime=%" PRIu64 ".%u",
26029edc611SAlan Somers 					in.body.setattr.atime,
26129edc611SAlan Somers 					in.body.setattr.atimensec);
26229edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_MTIME)
263cc04566cSAlan Somers 				printf(" mtime=%" PRIu64 ".%u",
26429edc611SAlan Somers 					in.body.setattr.mtime,
26529edc611SAlan Somers 					in.body.setattr.mtimensec);
26629edc611SAlan Somers 			if (in.body.setattr.valid & FATTR_FH)
26729edc611SAlan Somers 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
2689821f1d3SAlan Somers 			break;
269f067b609SAlan Somers 		case FUSE_SETLK:
270cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
271cc04566cSAlan Somers 				" type=%u pid=%u",
27229edc611SAlan Somers 				in.body.setlk.fh, in.body.setlk.owner,
27329edc611SAlan Somers 				in.body.setlk.lk.type,
27429edc611SAlan Somers 				in.body.setlk.lk.pid);
275f067b609SAlan Somers 			if (verbosity >= 2) {
276cc04566cSAlan Somers 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
27729edc611SAlan Somers 					in.body.setlk.lk.start,
27829edc611SAlan Somers 					in.body.setlk.lk.end);
279f067b609SAlan Somers 			}
280f067b609SAlan Somers 			break;
2819821f1d3SAlan Somers 		case FUSE_SETXATTR:
2829821f1d3SAlan Somers 			/*
2839821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
2849821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
2859821f1d3SAlan Somers 			 */
28629edc611SAlan Somers 			name = (const char*)in.body.bytes +
2879821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
28819ef317dSAlan Somers 			value = name + strlen(name) + 1;
28919ef317dSAlan Somers 			printf(" %s=%s", name, value);
2909821f1d3SAlan Somers 			break;
2919821f1d3SAlan Somers 		case FUSE_WRITE:
292cc04566cSAlan Somers 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
293d4fd0c81SAlan Somers 				" size=%u write_flags=%u",
29429edc611SAlan Somers 				in.body.write.fh,
29529edc611SAlan Somers 				in.body.write.offset, in.body.write.size,
29629edc611SAlan Somers 				in.body.write.write_flags);
297d4fd0c81SAlan Somers 			if (verbosity > 1)
298d4fd0c81SAlan Somers 				printf(" flags=%#x", in.body.write.flags);
2999821f1d3SAlan Somers 			break;
3009821f1d3SAlan Somers 		default:
3019821f1d3SAlan Somers 			break;
3029821f1d3SAlan Somers 	}
3039821f1d3SAlan Somers 	printf("\n");
3049821f1d3SAlan Somers }
3059821f1d3SAlan Somers 
306c2d70d6eSAlan Somers /*
307c2d70d6eSAlan Somers  * Debug a FUSE response.
308c2d70d6eSAlan Somers  *
309c2d70d6eSAlan Somers  * This is mostly useful for asynchronous notifications, which don't correspond
310c2d70d6eSAlan Somers  * to any request
311c2d70d6eSAlan Somers  */
312c2d70d6eSAlan Somers void MockFS::debug_response(const mockfs_buf_out &out) {
313c2d70d6eSAlan Somers 	const char *name;
314c2d70d6eSAlan Somers 
315c2d70d6eSAlan Somers 	if (verbosity == 0)
316c2d70d6eSAlan Somers 		return;
317c2d70d6eSAlan Somers 
318c2d70d6eSAlan Somers 	switch (out.header.error) {
319c2d70d6eSAlan Somers 		case FUSE_NOTIFY_INVAL_ENTRY:
320c2d70d6eSAlan Somers 			name = (const char*)out.body.bytes +
321c2d70d6eSAlan Somers 				sizeof(fuse_notify_inval_entry_out);
322c2d70d6eSAlan Somers 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
323c2d70d6eSAlan Somers 				out.body.inval_entry.parent, name);
324c2d70d6eSAlan Somers 			break;
325eae1ae13SAlan Somers 		case FUSE_NOTIFY_INVAL_INODE:
326eae1ae13SAlan Somers 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
327eae1ae13SAlan Somers 				" len=%" PRIi64 "\n",
328eae1ae13SAlan Somers 				out.body.inval_inode.ino,
329eae1ae13SAlan Somers 				out.body.inval_inode.off,
330eae1ae13SAlan Somers 				out.body.inval_inode.len);
331eae1ae13SAlan Somers 			break;
332c2d70d6eSAlan Somers 		default:
333c2d70d6eSAlan Somers 			break;
334c2d70d6eSAlan Somers 	}
335c2d70d6eSAlan Somers }
336c2d70d6eSAlan Somers 
33791ff3a0dSAlan Somers MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
33816bd2d47SAlan Somers 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
33916bd2d47SAlan Somers 	uint32_t kernel_minor_version)
3409821f1d3SAlan Somers {
3418b73a4c5SAlan Somers 	struct sigaction sa;
3429821f1d3SAlan Somers 	struct iovec *iov = NULL;
3439821f1d3SAlan Somers 	int iovlen = 0;
3449821f1d3SAlan Somers 	char fdstr[15];
34591ff3a0dSAlan Somers 	const bool trueval = true;
3469821f1d3SAlan Somers 
3479821f1d3SAlan Somers 	m_daemon_id = NULL;
34816bd2d47SAlan Somers 	m_kernel_minor_version = kernel_minor_version;
3499821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
3500a7c63e0SAlan Somers 	m_nready = -1;
3513429092cSAlan Somers 	m_pm = pm;
35281a619c4SAlan Somers 	m_quit = false;
3533429092cSAlan Somers 	if (m_pm == KQ)
3543429092cSAlan Somers 		m_kq = kqueue();
3553429092cSAlan Somers 	else
3563429092cSAlan Somers 		m_kq = -1;
3579821f1d3SAlan Somers 
3589821f1d3SAlan Somers 	/*
3599821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
3609821f1d3SAlan Somers 	 * mkdtemp
3619821f1d3SAlan Somers 	 */
3629821f1d3SAlan Somers 	/*
3639821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
3649821f1d3SAlan Somers 	 * instead.
3659821f1d3SAlan Somers 	 */
36691ff3a0dSAlan Somers 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
3679821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3689821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
3699821f1d3SAlan Somers 
3703429092cSAlan Somers 	switch (m_pm) {
3713429092cSAlan Somers 	case BLOCKING:
37291ff3a0dSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
3733429092cSAlan Somers 		break;
3743429092cSAlan Somers 	default:
3753429092cSAlan Somers 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
3763429092cSAlan Somers 		break;
3773429092cSAlan Somers 	}
3789821f1d3SAlan Somers 	if (m_fuse_fd < 0)
3799821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
3809821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
3819821f1d3SAlan Somers 
3829821f1d3SAlan Somers 	m_pid = getpid();
38391ff3a0dSAlan Somers 	m_child_pid = -1;
3849821f1d3SAlan Somers 
3859821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
3869821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
3879821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
3889821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
3893429092cSAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
3909821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
39191ff3a0dSAlan Somers 	if (allow_other) {
39291ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "allow_other",
3939821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
3949821f1d3SAlan Somers 	}
3959821f1d3SAlan Somers 	if (default_permissions) {
3969821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
3979821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
3989821f1d3SAlan Somers 	}
39991ff3a0dSAlan Somers 	if (push_symlinks_in) {
40091ff3a0dSAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
40191ff3a0dSAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
40291ff3a0dSAlan Somers 	}
403140bb492SAlan Somers 	if (ro) {
404140bb492SAlan Somers 		build_iovec(&iov, &iovlen, "ro",
405140bb492SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
406140bb492SAlan Somers 	}
4079821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
4089821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4099821f1d3SAlan Somers 			"Couldn't mount filesystem"));
4109821f1d3SAlan Somers 
4119821f1d3SAlan Somers 	// Setup default handler
4129821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
4139821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
4149821f1d3SAlan Somers 
4159821f1d3SAlan Somers 	init(flags);
4168b73a4c5SAlan Somers 	bzero(&sa, sizeof(sa));
4178b73a4c5SAlan Somers 	sa.sa_handler = sigint_handler;
4188b73a4c5SAlan Somers 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
4198b73a4c5SAlan Somers 	if (0 != sigaction(SIGUSR1, &sa, NULL))
4208b73a4c5SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4218b73a4c5SAlan Somers 			"Couldn't handle SIGUSR1"));
4229821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
4239821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
4249821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
4259821f1d3SAlan Somers }
4269821f1d3SAlan Somers 
4279821f1d3SAlan Somers MockFS::~MockFS() {
4289821f1d3SAlan Somers 	kill_daemon();
4299821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
4309821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
4319821f1d3SAlan Somers 		m_daemon_id = NULL;
4329821f1d3SAlan Somers 	}
4338b73a4c5SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
4349821f1d3SAlan Somers 	rmdir("mountpoint");
4353429092cSAlan Somers 	if (m_kq >= 0)
4363429092cSAlan Somers 		close(m_kq);
4379821f1d3SAlan Somers }
4389821f1d3SAlan Somers 
4399821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
44029edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
44129edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
4429821f1d3SAlan Somers 
44329edc611SAlan Somers 	read_request(*in);
4449821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
4459821f1d3SAlan Somers 
4469821f1d3SAlan Somers 	out->header.unique = in->header.unique;
4479821f1d3SAlan Somers 	out->header.error = 0;
4489821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
44916bd2d47SAlan Somers 	out->body.init.minor = m_kernel_minor_version;;
4509821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
4519821f1d3SAlan Somers 
4529821f1d3SAlan Somers 	/*
4539821f1d3SAlan Somers 	 * The default max_write is set to this formula in libfuse, though
4549821f1d3SAlan Somers 	 * individual filesystems can lower it.  The "- 4096" was added in
4559821f1d3SAlan Somers 	 * commit 154ffe2, with the commit message "fix".
4569821f1d3SAlan Somers 	 */
4579821f1d3SAlan Somers 	uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096;
4589821f1d3SAlan Somers 	/* For testing purposes, it should be distinct from MAXPHYS */
4599821f1d3SAlan Somers 	m_max_write = MIN(default_max_write, MAXPHYS / 2);
4609821f1d3SAlan Somers 	out->body.init.max_write = m_max_write;
4619821f1d3SAlan Somers 
4629821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
46329edc611SAlan Somers 	SET_OUT_HEADER_LEN(*out, init);
46429edc611SAlan Somers 	write(m_fuse_fd, out.get(), out->header.len);
4659821f1d3SAlan Somers }
4669821f1d3SAlan Somers 
4679821f1d3SAlan Somers void MockFS::kill_daemon() {
46881a619c4SAlan Somers 	m_quit = true;
4698b73a4c5SAlan Somers 	if (m_daemon_id != NULL)
4709821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
4718b73a4c5SAlan Somers 	// Closing the /dev/fuse file descriptor first allows unmount to
4728b73a4c5SAlan Somers 	// succeed even if the daemon doesn't correctly respond to commands
4738b73a4c5SAlan Somers 	// during the unmount sequence.
4749821f1d3SAlan Somers 	close(m_fuse_fd);
4758b73a4c5SAlan Somers 	m_fuse_fd = -1;
4769821f1d3SAlan Somers }
4779821f1d3SAlan Somers 
4789821f1d3SAlan Somers void MockFS::loop() {
47929edc611SAlan Somers 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
4809821f1d3SAlan Somers 
48129edc611SAlan Somers 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
4829821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
48381a619c4SAlan Somers 	while (!m_quit) {
48429edc611SAlan Somers 		bzero(in.get(), sizeof(*in));
48529edc611SAlan Somers 		read_request(*in);
48681a619c4SAlan Somers 		if (m_quit)
4879821f1d3SAlan Somers 			break;
4889821f1d3SAlan Somers 		if (verbosity > 0)
489c2d70d6eSAlan Somers 			debug_request(*in);
4909821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
49129edc611SAlan Somers 			process(*in, out);
4929821f1d3SAlan Somers 		} else {
4939821f1d3SAlan Somers 			/*
4949821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
4959821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
4969821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
4979821f1d3SAlan Somers 			 */
49899cf7bffSAlan Somers 			if (verbosity > 1)
49999cf7bffSAlan Somers 				printf("\tREJECTED (wrong pid %d)\n",
50099cf7bffSAlan Somers 					in->header.pid);
50129edc611SAlan Somers 			process_default(*in, out);
5029821f1d3SAlan Somers 		}
50329edc611SAlan Somers 		for (auto &it: out)
50429edc611SAlan Somers 			write_response(*it);
5059821f1d3SAlan Somers 		out.clear();
5069821f1d3SAlan Somers 	}
5079821f1d3SAlan Somers }
5089821f1d3SAlan Somers 
509c2d70d6eSAlan Somers int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
510c2d70d6eSAlan Somers {
511c2d70d6eSAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
512c2d70d6eSAlan Somers 
513c2d70d6eSAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
514c2d70d6eSAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
515c2d70d6eSAlan Somers 	out->body.inval_entry.parent = parent;
516c2d70d6eSAlan Somers 	out->body.inval_entry.namelen = namelen;
517c2d70d6eSAlan Somers 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
518c2d70d6eSAlan Somers 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
519c2d70d6eSAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
520c2d70d6eSAlan Somers 		namelen;
521c2d70d6eSAlan Somers 	debug_response(*out);
522c2d70d6eSAlan Somers 	write_response(*out);
523c2d70d6eSAlan Somers 	return 0;
524c2d70d6eSAlan Somers }
525c2d70d6eSAlan Somers 
526eae1ae13SAlan Somers int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
527eae1ae13SAlan Somers {
528eae1ae13SAlan Somers 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
529eae1ae13SAlan Somers 
530eae1ae13SAlan Somers 	out->header.unique = 0;	/* 0 means asynchronous notification */
531eae1ae13SAlan Somers 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
532eae1ae13SAlan Somers 	out->body.inval_inode.ino = ino;
533eae1ae13SAlan Somers 	out->body.inval_inode.off = off;
534eae1ae13SAlan Somers 	out->body.inval_inode.len = len;
535eae1ae13SAlan Somers 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
536eae1ae13SAlan Somers 	debug_response(*out);
537eae1ae13SAlan Somers 	write_response(*out);
538eae1ae13SAlan Somers 	return 0;
539eae1ae13SAlan Somers }
540eae1ae13SAlan Somers 
5419821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
5429821f1d3SAlan Somers 	if (pid == m_pid) {
5439821f1d3SAlan Somers 		return (true);
54491ff3a0dSAlan Somers 	} else if (pid == m_child_pid) {
54591ff3a0dSAlan Somers 		return (true);
5469821f1d3SAlan Somers 	} else {
5479821f1d3SAlan Somers 		struct kinfo_proc *ki;
5489821f1d3SAlan Somers 		bool ok = false;
5499821f1d3SAlan Somers 
5509821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
5519821f1d3SAlan Somers 		if (ki == NULL)
5529821f1d3SAlan Somers 			return (false);
5539821f1d3SAlan Somers 		/*
5549821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
5559821f1d3SAlan Somers 		 * can use aio functions
5569821f1d3SAlan Somers 		 */
5579821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
5589821f1d3SAlan Somers 			ok = true;
5599821f1d3SAlan Somers 		free(ki);
5609821f1d3SAlan Somers 		return (ok);
5619821f1d3SAlan Somers 	}
5629821f1d3SAlan Somers }
5639821f1d3SAlan Somers 
56429edc611SAlan Somers void MockFS::process_default(const mockfs_buf_in& in,
56529edc611SAlan Somers 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
5669821f1d3SAlan Somers {
56729edc611SAlan Somers 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
56829edc611SAlan Somers 	out0->header.unique = in.header.unique;
5699821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
5709821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
57129edc611SAlan Somers 	out.push_back(std::move(out0));
5729821f1d3SAlan Somers }
5739821f1d3SAlan Somers 
57429edc611SAlan Somers void MockFS::read_request(mockfs_buf_in &in) {
5759821f1d3SAlan Somers 	ssize_t res;
57677fbe694SAlan Somers 	int nready = 0;
5773429092cSAlan Somers 	fd_set readfds;
5783429092cSAlan Somers 	pollfd fds[1];
5793429092cSAlan Somers 	struct kevent changes[1];
5803429092cSAlan Somers 	struct kevent events[1];
58177fbe694SAlan Somers 	struct timespec timeout_ts;
58277fbe694SAlan Somers 	struct timeval timeout_tv;
58377fbe694SAlan Somers 	const int timeout_ms = 999;
58477fbe694SAlan Somers 	int timeout_int, nfds;
5859821f1d3SAlan Somers 
5863429092cSAlan Somers 	switch (m_pm) {
5873429092cSAlan Somers 	case BLOCKING:
5883429092cSAlan Somers 		break;
5893429092cSAlan Somers 	case KQ:
59077fbe694SAlan Somers 		timeout_ts.tv_sec = 0;
59177fbe694SAlan Somers 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
59277fbe694SAlan Somers 		while (nready == 0) {
59377fbe694SAlan Somers 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
59477fbe694SAlan Somers 				0, 0);
59577fbe694SAlan Somers 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
59677fbe694SAlan Somers 				&timeout_ts);
5973429092cSAlan Somers 			if (m_quit)
5983429092cSAlan Somers 				return;
59977fbe694SAlan Somers 		}
6003429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6013429092cSAlan Somers 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
6023429092cSAlan Somers 		if (events[0].flags & EV_ERROR)
6033429092cSAlan Somers 			FAIL() << strerror(events[0].data);
6043429092cSAlan Somers 		else if (events[0].flags & EV_EOF)
6053429092cSAlan Somers 			FAIL() << strerror(events[0].fflags);
6060a7c63e0SAlan Somers 		m_nready = events[0].data;
6073429092cSAlan Somers 		break;
6083429092cSAlan Somers 	case POLL:
60977fbe694SAlan Somers 		timeout_int = timeout_ms;
6103429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
6113429092cSAlan Somers 		fds[0].events = POLLIN;
61277fbe694SAlan Somers 		while (nready == 0) {
61377fbe694SAlan Somers 			nready = poll(fds, 1, timeout_int);
6143429092cSAlan Somers 			if (m_quit)
6153429092cSAlan Somers 				return;
61677fbe694SAlan Somers 		}
6173429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6183429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLIN);
6193429092cSAlan Somers 		break;
6203429092cSAlan Somers 	case SELECT:
62177fbe694SAlan Somers 		timeout_tv.tv_sec = 0;
62277fbe694SAlan Somers 		timeout_tv.tv_usec = timeout_ms * 1'000;
62377fbe694SAlan Somers 		nfds = m_fuse_fd + 1;
62477fbe694SAlan Somers 		while (nready == 0) {
6253429092cSAlan Somers 			FD_ZERO(&readfds);
6263429092cSAlan Somers 			FD_SET(m_fuse_fd, &readfds);
62777fbe694SAlan Somers 			nready = select(nfds, &readfds, NULL, NULL,
62877fbe694SAlan Somers 				&timeout_tv);
6293429092cSAlan Somers 			if (m_quit)
6303429092cSAlan Somers 				return;
63177fbe694SAlan Somers 		}
6323429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6333429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
6343429092cSAlan Somers 		break;
6353429092cSAlan Somers 	default:
6363429092cSAlan Somers 		FAIL() << "not yet implemented";
6373429092cSAlan Somers 	}
63829edc611SAlan Somers 	res = read(m_fuse_fd, &in, sizeof(in));
6393429092cSAlan Somers 
640*b690d120SAlan Somers 	if (res < 0 && !m_quit) {
641*b690d120SAlan Somers 		FAIL() << "read: " << strerror(errno);
642*b690d120SAlan Somers 		m_quit = true;
643*b690d120SAlan Somers 	}
64429edc611SAlan Somers 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
6459821f1d3SAlan Somers }
6469821f1d3SAlan Somers 
64729edc611SAlan Somers void MockFS::write_response(const mockfs_buf_out &out) {
6483429092cSAlan Somers 	fd_set writefds;
6493429092cSAlan Somers 	pollfd fds[1];
6503429092cSAlan Somers 	int nready, nfds;
6513429092cSAlan Somers 	ssize_t r;
6523429092cSAlan Somers 
6533429092cSAlan Somers 	switch (m_pm) {
6543429092cSAlan Somers 	case BLOCKING:
6553429092cSAlan Somers 	case KQ:	/* EVFILT_WRITE is not supported */
6563429092cSAlan Somers 		break;
6573429092cSAlan Somers 	case POLL:
6583429092cSAlan Somers 		fds[0].fd = m_fuse_fd;
6593429092cSAlan Somers 		fds[0].events = POLLOUT;
6603429092cSAlan Somers 		nready = poll(fds, 1, INFTIM);
6613429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6623429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
6633429092cSAlan Somers 		ASSERT_TRUE(fds[0].revents & POLLOUT);
6643429092cSAlan Somers 		break;
6653429092cSAlan Somers 	case SELECT:
6663429092cSAlan Somers 		FD_ZERO(&writefds);
6673429092cSAlan Somers 		FD_SET(m_fuse_fd, &writefds);
6683429092cSAlan Somers 		nfds = m_fuse_fd + 1;
6693429092cSAlan Somers 		nready = select(nfds, NULL, &writefds, NULL, NULL);
6703429092cSAlan Somers 		ASSERT_LE(0, nready) << strerror(errno);
6713429092cSAlan Somers 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
6723429092cSAlan Somers 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
6733429092cSAlan Somers 		break;
6743429092cSAlan Somers 	default:
6753429092cSAlan Somers 		FAIL() << "not yet implemented";
6763429092cSAlan Somers 	}
67729edc611SAlan Somers 	r = write(m_fuse_fd, &out, out.header.len);
6783429092cSAlan Somers 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
6793429092cSAlan Somers }
6803429092cSAlan Somers 
6819821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
6829821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
6839821f1d3SAlan Somers 
6849821f1d3SAlan Somers 	mock_fs->loop();
6859821f1d3SAlan Somers 
6869821f1d3SAlan Somers 	return (NULL);
6879821f1d3SAlan Somers }
6889821f1d3SAlan Somers 
6899821f1d3SAlan Somers void MockFS::unmount() {
6909821f1d3SAlan Somers 	::unmount("mountpoint", 0);
6919821f1d3SAlan Somers }
692