xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 54c1a65736ec012b583ade1d53c477e182c574e4)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 The FreeBSD Foundation
5  *
6  * This software was developed by BFF Storage Systems, LLC under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32 
33 extern "C" {
34 #include <sys/param.h>
35 
36 #include <sys/mount.h>
37 #include <sys/select.h>
38 #include <sys/stat.h>
39 #include <sys/uio.h>
40 #include <sys/user.h>
41 
42 #include <fcntl.h>
43 #include <libutil.h>
44 #include <poll.h>
45 #include <pthread.h>
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 
50 #include "mntopts.h"	// for build_iovec
51 }
52 
53 #include <cinttypes>
54 
55 #include <gtest/gtest.h>
56 
57 #include "mockfs.hh"
58 
59 using namespace testing;
60 
61 int verbosity = 0;
62 
63 const char* opcode2opname(uint32_t opcode)
64 {
65 	const char* table[] = {
66 		"Unknown (opcode 0)",
67 		"LOOKUP",
68 		"FORGET",
69 		"GETATTR",
70 		"SETATTR",
71 		"READLINK",
72 		"SYMLINK",
73 		"Unknown (opcode 7)",
74 		"MKNOD",
75 		"MKDIR",
76 		"UNLINK",
77 		"RMDIR",
78 		"RENAME",
79 		"LINK",
80 		"OPEN",
81 		"READ",
82 		"WRITE",
83 		"STATFS",
84 		"RELEASE",
85 		"Unknown (opcode 19)",
86 		"FSYNC",
87 		"SETXATTR",
88 		"GETXATTR",
89 		"LISTXATTR",
90 		"REMOVEXATTR",
91 		"FLUSH",
92 		"INIT",
93 		"OPENDIR",
94 		"READDIR",
95 		"RELEASEDIR",
96 		"FSYNCDIR",
97 		"GETLK",
98 		"SETLK",
99 		"SETLKW",
100 		"ACCESS",
101 		"CREATE",
102 		"INTERRUPT",
103 		"BMAP",
104 		"DESTROY",
105 		"IOCTL",
106 		"POLL",
107 		"NOTIFY_REPLY",
108 		"BATCH_FORGET",
109 		"FALLOCATE",
110 		"READDIRPLUS",
111 		"RENAME2",
112 		"LSEEK",
113 		"COPY_FILE_RANGE",
114 	};
115 	if (opcode >= nitems(table))
116 		return ("Unknown (opcode > max)");
117 	else
118 		return (table[opcode]);
119 }
120 
121 ProcessMockerT
122 ReturnErrno(int error)
123 {
124 	return([=](auto in, auto &out) {
125 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
126 		out0->header.unique = in.header.unique;
127 		out0->header.error = -error;
128 		out0->header.len = sizeof(out0->header);
129 		out.push_back(std::move(out0));
130 	});
131 }
132 
133 /* Helper function used for returning negative cache entries for LOOKUP */
134 ProcessMockerT
135 ReturnNegativeCache(const struct timespec *entry_valid)
136 {
137 	return([=](auto in, auto &out) {
138 		/* nodeid means ENOENT and cache it */
139 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
140 		out0->body.entry.nodeid = 0;
141 		out0->header.unique = in.header.unique;
142 		out0->header.error = 0;
143 		out0->body.entry.entry_valid = entry_valid->tv_sec;
144 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
145 		SET_OUT_HEADER_LEN(*out0, entry);
146 		out.push_back(std::move(out0));
147 	});
148 }
149 
150 ProcessMockerT
151 ReturnImmediate(std::function<void(const mockfs_buf_in& in,
152 				   struct mockfs_buf_out &out)> f)
153 {
154 	return([=](auto& in, auto &out) {
155 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
156 		out0->header.unique = in.header.unique;
157 		f(in, *out0);
158 		out.push_back(std::move(out0));
159 	});
160 }
161 
162 void sigint_handler(int __unused sig) {
163 	// Don't do anything except interrupt the daemon's read(2) call
164 }
165 
166 void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
167 {
168 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
169 		in.header.nodeid);
170 	if (verbosity > 1) {
171 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
172 			" buflen=%zd",
173 			in.header.uid, in.header.gid, in.header.pid,
174 			in.header.unique, in.header.len, buflen);
175 	}
176 	switch (in.header.opcode) {
177 		const char *name, *value;
178 
179 		case FUSE_ACCESS:
180 			printf(" mask=%#x", in.body.access.mask);
181 			break;
182 		case FUSE_BMAP:
183 			printf(" block=%" PRIx64 " blocksize=%#x",
184 				in.body.bmap.block, in.body.bmap.blocksize);
185 			break;
186 		case FUSE_COPY_FILE_RANGE:
187 			printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
188 			       " off_out=%" PRIu64 " size=%" PRIu64,
189 			       in.body.copy_file_range.off_in,
190 			       in.body.copy_file_range.nodeid_out,
191 			       in.body.copy_file_range.off_out,
192 			       in.body.copy_file_range.len);
193 			if (verbosity > 1)
194 				printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
195 				       " flags=%" PRIx64,
196 				       in.body.copy_file_range.fh_in,
197 				       in.body.copy_file_range.fh_out,
198 				       in.body.copy_file_range.flags);
199 			break;
200 		case FUSE_CREATE:
201 			if (m_kernel_minor_version >= 12)
202 				name = (const char*)in.body.bytes +
203 					sizeof(fuse_create_in);
204 			else
205 				name = (const char*)in.body.bytes +
206 					sizeof(fuse_open_in);
207 			printf(" flags=%#x name=%s",
208 				in.body.open.flags, name);
209 			break;
210 		case FUSE_FLUSH:
211 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
212 				in.body.flush.fh,
213 				in.body.flush.lock_owner);
214 			break;
215 		case FUSE_FORGET:
216 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
217 			break;
218 		case FUSE_FSYNC:
219 			printf(" flags=%#x", in.body.fsync.fsync_flags);
220 			break;
221 		case FUSE_FSYNCDIR:
222 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
223 			break;
224 		case FUSE_INTERRUPT:
225 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
226 			break;
227 		case FUSE_LINK:
228 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
229 			break;
230 		case FUSE_LISTXATTR:
231 			printf(" size=%" PRIu32, in.body.listxattr.size);
232 			break;
233 		case FUSE_LOOKUP:
234 			printf(" %s", in.body.lookup);
235 			break;
236 		case FUSE_LSEEK:
237 			switch (in.body.lseek.whence) {
238 			case SEEK_HOLE:
239 				printf(" SEEK_HOLE offset=%jd",
240 				    in.body.lseek.offset);
241 				break;
242 			case SEEK_DATA:
243 				printf(" SEEK_DATA offset=%jd",
244 				    in.body.lseek.offset);
245 				break;
246 			default:
247 				printf(" whence=%u offset=%jd",
248 				    in.body.lseek.whence, in.body.lseek.offset);
249 				break;
250 			}
251 			break;
252 		case FUSE_MKDIR:
253 			name = (const char*)in.body.bytes +
254 				sizeof(fuse_mkdir_in);
255 			printf(" name=%s mode=%#o umask=%#o", name,
256 				in.body.mkdir.mode, in.body.mkdir.umask);
257 			break;
258 		case FUSE_MKNOD:
259 			if (m_kernel_minor_version >= 12)
260 				name = (const char*)in.body.bytes +
261 					sizeof(fuse_mknod_in);
262 			else
263 				name = (const char*)in.body.bytes +
264 					FUSE_COMPAT_MKNOD_IN_SIZE;
265 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
266 				in.body.mknod.mode, in.body.mknod.rdev,
267 				in.body.mknod.umask, name);
268 			break;
269 		case FUSE_OPEN:
270 			printf(" flags=%#x", in.body.open.flags);
271 			break;
272 		case FUSE_OPENDIR:
273 			printf(" flags=%#x", in.body.opendir.flags);
274 			break;
275 		case FUSE_READ:
276 			printf(" offset=%" PRIu64 " size=%u",
277 				in.body.read.offset,
278 				in.body.read.size);
279 			if (verbosity > 1)
280 				printf(" flags=%#x", in.body.read.flags);
281 			break;
282 		case FUSE_READDIR:
283 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
284 				in.body.readdir.fh, in.body.readdir.offset,
285 				in.body.readdir.size);
286 			break;
287 		case FUSE_RELEASE:
288 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
289 				in.body.release.fh,
290 				in.body.release.flags,
291 				in.body.release.lock_owner);
292 			break;
293 		case FUSE_SETATTR:
294 			if (verbosity <= 1) {
295 				printf(" valid=%#x", in.body.setattr.valid);
296 				break;
297 			}
298 			if (in.body.setattr.valid & FATTR_MODE)
299 				printf(" mode=%#o", in.body.setattr.mode);
300 			if (in.body.setattr.valid & FATTR_UID)
301 				printf(" uid=%u", in.body.setattr.uid);
302 			if (in.body.setattr.valid & FATTR_GID)
303 				printf(" gid=%u", in.body.setattr.gid);
304 			if (in.body.setattr.valid & FATTR_SIZE)
305 				printf(" size=%" PRIu64, in.body.setattr.size);
306 			if (in.body.setattr.valid & FATTR_ATIME)
307 				printf(" atime=%" PRIu64 ".%u",
308 					in.body.setattr.atime,
309 					in.body.setattr.atimensec);
310 			if (in.body.setattr.valid & FATTR_MTIME)
311 				printf(" mtime=%" PRIu64 ".%u",
312 					in.body.setattr.mtime,
313 					in.body.setattr.mtimensec);
314 			if (in.body.setattr.valid & FATTR_FH)
315 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
316 			break;
317 		case FUSE_SETLK:
318 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
319 				" type=%u pid=%u",
320 				in.body.setlk.fh, in.body.setlk.owner,
321 				in.body.setlk.lk.type,
322 				in.body.setlk.lk.pid);
323 			if (verbosity >= 2) {
324 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
325 					in.body.setlk.lk.start,
326 					in.body.setlk.lk.end);
327 			}
328 			break;
329 		case FUSE_SETXATTR:
330 			/*
331 			 * In theory neither the xattr name and value need be
332 			 * ASCII, but in this test suite they always are.
333 			 */
334 			name = (const char*)in.body.bytes +
335 				sizeof(fuse_setxattr_in);
336 			value = name + strlen(name) + 1;
337 			printf(" %s=%s", name, value);
338 			break;
339 		case FUSE_WRITE:
340 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
341 				" size=%u write_flags=%u",
342 				in.body.write.fh,
343 				in.body.write.offset, in.body.write.size,
344 				in.body.write.write_flags);
345 			if (verbosity > 1)
346 				printf(" flags=%#x", in.body.write.flags);
347 			break;
348 		default:
349 			break;
350 	}
351 	printf("\n");
352 }
353 
354 /*
355  * Debug a FUSE response.
356  *
357  * This is mostly useful for asynchronous notifications, which don't correspond
358  * to any request
359  */
360 void MockFS::debug_response(const mockfs_buf_out &out) {
361 	const char *name;
362 
363 	if (verbosity == 0)
364 		return;
365 
366 	switch (out.header.error) {
367 		case FUSE_NOTIFY_INVAL_ENTRY:
368 			name = (const char*)out.body.bytes +
369 				sizeof(fuse_notify_inval_entry_out);
370 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
371 				out.body.inval_entry.parent, name);
372 			break;
373 		case FUSE_NOTIFY_INVAL_INODE:
374 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
375 				" len=%" PRIi64 "\n",
376 				out.body.inval_inode.ino,
377 				out.body.inval_inode.off,
378 				out.body.inval_inode.len);
379 			break;
380 		case FUSE_NOTIFY_STORE:
381 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
382 				" size=%" PRIu32 "\n",
383 				out.body.store.nodeid,
384 				out.body.store.offset,
385 				out.body.store.size);
386 			break;
387 		default:
388 			break;
389 	}
390 }
391 
392 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
393 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
394 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
395 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime)
396 {
397 	struct sigaction sa;
398 	struct iovec *iov = NULL;
399 	int iovlen = 0;
400 	char fdstr[15];
401 	const bool trueval = true;
402 
403 	m_daemon_id = NULL;
404 	m_kernel_minor_version = kernel_minor_version;
405 	m_maxreadahead = max_readahead;
406 	m_maxwrite = MIN(max_write, max_max_write);
407 	m_nready = -1;
408 	m_pm = pm;
409 	m_time_gran = time_gran;
410 	m_quit = false;
411 	m_last_unique = 0;
412 	if (m_pm == KQ)
413 		m_kq = kqueue();
414 	else
415 		m_kq = -1;
416 
417 	/*
418 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
419 	 * mkdtemp
420 	 */
421 	/*
422 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
423 	 * instead.
424 	 */
425 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
426 		throw(std::system_error(errno, std::system_category(),
427 			"Couldn't make mountpoint directory"));
428 
429 	switch (m_pm) {
430 	case BLOCKING:
431 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
432 		break;
433 	default:
434 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
435 		break;
436 	}
437 	if (m_fuse_fd < 0)
438 		throw(std::system_error(errno, std::system_category(),
439 			"Couldn't open /dev/fuse"));
440 
441 	m_pid = getpid();
442 	m_child_pid = -1;
443 
444 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
445 	build_iovec(&iov, &iovlen, "fspath",
446 		    __DECONST(void *, "mountpoint"), -1);
447 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
448 	sprintf(fdstr, "%d", m_fuse_fd);
449 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
450 	if (allow_other) {
451 		build_iovec(&iov, &iovlen, "allow_other",
452 			__DECONST(void*, &trueval), sizeof(bool));
453 	}
454 	if (default_permissions) {
455 		build_iovec(&iov, &iovlen, "default_permissions",
456 			__DECONST(void*, &trueval), sizeof(bool));
457 	}
458 	if (push_symlinks_in) {
459 		build_iovec(&iov, &iovlen, "push_symlinks_in",
460 			__DECONST(void*, &trueval), sizeof(bool));
461 	}
462 	if (ro) {
463 		build_iovec(&iov, &iovlen, "ro",
464 			__DECONST(void*, &trueval), sizeof(bool));
465 	}
466 	if (async) {
467 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
468 			sizeof(bool));
469 	}
470 	if (noatime) {
471 		build_iovec(&iov, &iovlen, "noatime",
472 			__DECONST(void*, &trueval), sizeof(bool));
473 	}
474 	if (noclusterr) {
475 		build_iovec(&iov, &iovlen, "noclusterr",
476 			__DECONST(void*, &trueval), sizeof(bool));
477 	}
478 	if (nointr) {
479 		build_iovec(&iov, &iovlen, "nointr",
480 			__DECONST(void*, &trueval), sizeof(bool));
481 	} else {
482 		build_iovec(&iov, &iovlen, "intr",
483 			__DECONST(void*, &trueval), sizeof(bool));
484 	}
485 	if (nmount(iov, iovlen, 0))
486 		throw(std::system_error(errno, std::system_category(),
487 			"Couldn't mount filesystem"));
488 
489 	// Setup default handler
490 	ON_CALL(*this, process(_, _))
491 		.WillByDefault(Invoke(this, &MockFS::process_default));
492 
493 	init(flags);
494 	bzero(&sa, sizeof(sa));
495 	sa.sa_handler = sigint_handler;
496 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
497 	if (0 != sigaction(SIGUSR1, &sa, NULL))
498 		throw(std::system_error(errno, std::system_category(),
499 			"Couldn't handle SIGUSR1"));
500 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
501 		throw(std::system_error(errno, std::system_category(),
502 			"Couldn't Couldn't start fuse thread"));
503 }
504 
505 MockFS::~MockFS() {
506 	kill_daemon();
507 	if (m_daemon_id != NULL) {
508 		pthread_join(m_daemon_id, NULL);
509 		m_daemon_id = NULL;
510 	}
511 	::unmount("mountpoint", MNT_FORCE);
512 	rmdir("mountpoint");
513 	if (m_kq >= 0)
514 		close(m_kq);
515 }
516 
517 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
518 	uint32_t inlen = in.header.len;
519 	size_t fih = sizeof(in.header);
520 	switch (in.header.opcode) {
521 	case FUSE_LOOKUP:
522 	case FUSE_RMDIR:
523 	case FUSE_SYMLINK:
524 	case FUSE_UNLINK:
525 		EXPECT_GT(inlen, fih) << "Missing request filename";
526 		// No redundant information for checking buflen
527 		break;
528 	case FUSE_FORGET:
529 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
530 		EXPECT_EQ((size_t)buflen, inlen);
531 		break;
532 	case FUSE_GETATTR:
533 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
534 		EXPECT_EQ((size_t)buflen, inlen);
535 		break;
536 	case FUSE_SETATTR:
537 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
538 		EXPECT_EQ((size_t)buflen, inlen);
539 		break;
540 	case FUSE_READLINK:
541 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
542 		EXPECT_EQ((size_t)buflen, inlen);
543 		break;
544 	case FUSE_MKNOD:
545 		{
546 			size_t s;
547 			if (m_kernel_minor_version >= 12)
548 				s = sizeof(in.body.mknod);
549 			else
550 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
551 			EXPECT_GE(inlen, fih + s) << "Missing request body";
552 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
553 			// No redundant information for checking buflen
554 			break;
555 		}
556 	case FUSE_MKDIR:
557 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
558 			"Missing request body";
559 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
560 			"Missing request filename";
561 		// No redundant information for checking buflen
562 		break;
563 	case FUSE_RENAME:
564 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
565 			"Missing request body";
566 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
567 			"Missing request filename";
568 		// No redundant information for checking buflen
569 		break;
570 	case FUSE_LINK:
571 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
572 			"Missing request body";
573 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
574 			"Missing request filename";
575 		// No redundant information for checking buflen
576 		break;
577 	case FUSE_OPEN:
578 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
579 		EXPECT_EQ((size_t)buflen, inlen);
580 		break;
581 	case FUSE_READ:
582 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
583 		EXPECT_EQ((size_t)buflen, inlen);
584 		break;
585 	case FUSE_WRITE:
586 		{
587 			size_t s;
588 
589 			if (m_kernel_minor_version >= 9)
590 				s = sizeof(in.body.write);
591 			else
592 				s = FUSE_COMPAT_WRITE_IN_SIZE;
593 			// I suppose a 0-byte write should be allowed
594 			EXPECT_GE(inlen, fih + s) << "Missing request body";
595 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
596 			break;
597 		}
598 	case FUSE_DESTROY:
599 	case FUSE_STATFS:
600 		EXPECT_EQ(inlen, fih);
601 		EXPECT_EQ((size_t)buflen, inlen);
602 		break;
603 	case FUSE_RELEASE:
604 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
605 		EXPECT_EQ((size_t)buflen, inlen);
606 		break;
607 	case FUSE_FSYNC:
608 	case FUSE_FSYNCDIR:
609 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
610 		EXPECT_EQ((size_t)buflen, inlen);
611 		break;
612 	case FUSE_SETXATTR:
613 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
614 			"Missing request body";
615 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
616 			"Missing request attribute name";
617 		// No redundant information for checking buflen
618 		break;
619 	case FUSE_GETXATTR:
620 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
621 			"Missing request body";
622 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
623 			"Missing request attribute name";
624 		// No redundant information for checking buflen
625 		break;
626 	case FUSE_LISTXATTR:
627 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
628 		EXPECT_EQ((size_t)buflen, inlen);
629 		break;
630 	case FUSE_REMOVEXATTR:
631 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
632 		// No redundant information for checking buflen
633 		break;
634 	case FUSE_FLUSH:
635 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
636 		EXPECT_EQ((size_t)buflen, inlen);
637 		break;
638 	case FUSE_INIT:
639 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
640 		EXPECT_EQ((size_t)buflen, inlen);
641 		break;
642 	case FUSE_OPENDIR:
643 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
644 		EXPECT_EQ((size_t)buflen, inlen);
645 		break;
646 	case FUSE_READDIR:
647 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
648 		EXPECT_EQ((size_t)buflen, inlen);
649 		break;
650 	case FUSE_RELEASEDIR:
651 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
652 		EXPECT_EQ((size_t)buflen, inlen);
653 		break;
654 	case FUSE_GETLK:
655 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
656 		EXPECT_EQ((size_t)buflen, inlen);
657 		break;
658 	case FUSE_SETLK:
659 	case FUSE_SETLKW:
660 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
661 		EXPECT_EQ((size_t)buflen, inlen);
662 		break;
663 	case FUSE_ACCESS:
664 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
665 		EXPECT_EQ((size_t)buflen, inlen);
666 		break;
667 	case FUSE_CREATE:
668 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
669 			"Missing request body";
670 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
671 			"Missing request filename";
672 		// No redundant information for checking buflen
673 		break;
674 	case FUSE_INTERRUPT:
675 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
676 		EXPECT_EQ((size_t)buflen, inlen);
677 		break;
678 	case FUSE_BMAP:
679 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
680 		EXPECT_EQ((size_t)buflen, inlen);
681 		break;
682 	case FUSE_LSEEK:
683 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
684 		EXPECT_EQ((size_t)buflen, inlen);
685 		break;
686 	case FUSE_COPY_FILE_RANGE:
687 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
688 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
689 		EXPECT_EQ((size_t)buflen, inlen);
690 		break;
691 	case FUSE_NOTIFY_REPLY:
692 	case FUSE_BATCH_FORGET:
693 	case FUSE_FALLOCATE:
694 	case FUSE_IOCTL:
695 	case FUSE_POLL:
696 	case FUSE_READDIRPLUS:
697 		FAIL() << "Unsupported opcode?";
698 	default:
699 		FAIL() << "Unknown opcode " << in.header.opcode;
700 	}
701 	/*
702 	 * Check that the ticket's unique value is sequential.  Technically it
703 	 * doesn't need to be sequential, merely unique.  But the current
704 	 * fusefs driver _does_ make it sequential, and that's easy to check
705 	 * for.
706 	 */
707 	if (in.header.unique != ++m_last_unique)
708 		FAIL() << "Non-sequential unique value";
709 }
710 
711 void MockFS::init(uint32_t flags) {
712 	ssize_t buflen;
713 
714 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
715 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
716 
717 	read_request(*in, buflen);
718 	if (verbosity > 0)
719 		debug_request(*in, buflen);
720 	audit_request(*in, buflen);
721 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
722 
723 	out->header.unique = in->header.unique;
724 	out->header.error = 0;
725 	out->body.init.major = FUSE_KERNEL_VERSION;
726 	out->body.init.minor = m_kernel_minor_version;;
727 	out->body.init.flags = in->body.init.flags & flags;
728 	out->body.init.max_write = m_maxwrite;
729 	out->body.init.max_readahead = m_maxreadahead;
730 
731 	if (m_kernel_minor_version < 23) {
732 		SET_OUT_HEADER_LEN(*out, init_7_22);
733 	} else {
734 		out->body.init.time_gran = m_time_gran;
735 		SET_OUT_HEADER_LEN(*out, init);
736 	}
737 
738 	write(m_fuse_fd, out.get(), out->header.len);
739 }
740 
741 void MockFS::kill_daemon() {
742 	m_quit = true;
743 	if (m_daemon_id != NULL)
744 		pthread_kill(m_daemon_id, SIGUSR1);
745 	// Closing the /dev/fuse file descriptor first allows unmount to
746 	// succeed even if the daemon doesn't correctly respond to commands
747 	// during the unmount sequence.
748 	close(m_fuse_fd);
749 	m_fuse_fd = -1;
750 }
751 
752 void MockFS::loop() {
753 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
754 
755 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
756 	ASSERT_TRUE(in != NULL);
757 	while (!m_quit) {
758 		ssize_t buflen;
759 
760 		bzero(in.get(), sizeof(*in));
761 		read_request(*in, buflen);
762 		if (m_quit)
763 			break;
764 		if (verbosity > 0)
765 			debug_request(*in, buflen);
766 		audit_request(*in, buflen);
767 		if (pid_ok((pid_t)in->header.pid)) {
768 			process(*in, out);
769 		} else {
770 			/*
771 			 * Reject any requests from unknown processes.  Because
772 			 * we actually do mount a filesystem, plenty of
773 			 * unrelated system daemons may try to access it.
774 			 */
775 			if (verbosity > 1)
776 				printf("\tREJECTED (wrong pid %d)\n",
777 					in->header.pid);
778 			process_default(*in, out);
779 		}
780 		for (auto &it: out)
781 			write_response(*it);
782 		out.clear();
783 	}
784 }
785 
786 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
787 {
788 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
789 
790 	out->header.unique = 0;	/* 0 means asynchronous notification */
791 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
792 	out->body.inval_entry.parent = parent;
793 	out->body.inval_entry.namelen = namelen;
794 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
795 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
796 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
797 		namelen;
798 	debug_response(*out);
799 	write_response(*out);
800 	return 0;
801 }
802 
803 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
804 {
805 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
806 
807 	out->header.unique = 0;	/* 0 means asynchronous notification */
808 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
809 	out->body.inval_inode.ino = ino;
810 	out->body.inval_inode.off = off;
811 	out->body.inval_inode.len = len;
812 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
813 	debug_response(*out);
814 	write_response(*out);
815 	return 0;
816 }
817 
818 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
819 {
820 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
821 
822 	out->header.unique = 0;	/* 0 means asynchronous notification */
823 	out->header.error = FUSE_NOTIFY_STORE;
824 	out->body.store.nodeid = ino;
825 	out->body.store.offset = off;
826 	out->body.store.size = size;
827 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
828 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
829 	debug_response(*out);
830 	write_response(*out);
831 	return 0;
832 }
833 
834 bool MockFS::pid_ok(pid_t pid) {
835 	if (pid == m_pid) {
836 		return (true);
837 	} else if (pid == m_child_pid) {
838 		return (true);
839 	} else {
840 		struct kinfo_proc *ki;
841 		bool ok = false;
842 
843 		ki = kinfo_getproc(pid);
844 		if (ki == NULL)
845 			return (false);
846 		/*
847 		 * Allow access by the aio daemon processes so that our tests
848 		 * can use aio functions
849 		 */
850 		if (0 == strncmp("aiod", ki->ki_comm, 4))
851 			ok = true;
852 		free(ki);
853 		return (ok);
854 	}
855 }
856 
857 void MockFS::process_default(const mockfs_buf_in& in,
858 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
859 {
860 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
861 	out0->header.unique = in.header.unique;
862 	out0->header.error = -EOPNOTSUPP;
863 	out0->header.len = sizeof(out0->header);
864 	out.push_back(std::move(out0));
865 }
866 
867 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
868 	int nready = 0;
869 	fd_set readfds;
870 	pollfd fds[1];
871 	struct kevent changes[1];
872 	struct kevent events[1];
873 	struct timespec timeout_ts;
874 	struct timeval timeout_tv;
875 	const int timeout_ms = 999;
876 	int timeout_int, nfds;
877 	int fuse_fd;
878 
879 	switch (m_pm) {
880 	case BLOCKING:
881 		break;
882 	case KQ:
883 		timeout_ts.tv_sec = 0;
884 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
885 		while (nready == 0) {
886 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
887 				EV_ADD | EV_ONESHOT, 0, 0, 0);
888 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
889 				&timeout_ts);
890 			if (m_quit)
891 				return;
892 		}
893 		ASSERT_LE(0, nready) << strerror(errno);
894 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
895 		if (events[0].flags & EV_ERROR)
896 			FAIL() << strerror(events[0].data);
897 		else if (events[0].flags & EV_EOF)
898 			FAIL() << strerror(events[0].fflags);
899 		m_nready = events[0].data;
900 		break;
901 	case POLL:
902 		timeout_int = timeout_ms;
903 		fds[0].fd = m_fuse_fd;
904 		fds[0].events = POLLIN;
905 		while (nready == 0) {
906 			nready = poll(fds, 1, timeout_int);
907 			if (m_quit)
908 				return;
909 		}
910 		ASSERT_LE(0, nready) << strerror(errno);
911 		ASSERT_TRUE(fds[0].revents & POLLIN);
912 		break;
913 	case SELECT:
914 		fuse_fd = m_fuse_fd;
915 		if (fuse_fd < 0)
916 			break;
917 		timeout_tv.tv_sec = 0;
918 		timeout_tv.tv_usec = timeout_ms * 1'000;
919 		nfds = fuse_fd + 1;
920 		while (nready == 0) {
921 			FD_ZERO(&readfds);
922 			FD_SET(fuse_fd, &readfds);
923 			nready = select(nfds, &readfds, NULL, NULL,
924 				&timeout_tv);
925 			if (m_quit)
926 				return;
927 		}
928 		ASSERT_LE(0, nready) << strerror(errno);
929 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
930 		break;
931 	default:
932 		FAIL() << "not yet implemented";
933 	}
934 	res = read(m_fuse_fd, &in, sizeof(in));
935 
936 	if (res < 0 && !m_quit) {
937 		m_quit = true;
938 		FAIL() << "read: " << strerror(errno);
939 	}
940 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
941 	/*
942 	 * Inconsistently, fuse_in_header.len is the size of the entire
943 	 * request,including header, even though fuse_out_header.len excludes
944 	 * the size of the header.
945 	 */
946 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
947 }
948 
949 void MockFS::write_response(const mockfs_buf_out &out) {
950 	fd_set writefds;
951 	pollfd fds[1];
952 	struct kevent changes[1];
953 	struct kevent events[1];
954 	int nready, nfds;
955 	ssize_t r;
956 
957 	switch (m_pm) {
958 	case BLOCKING:
959 		break;
960 	case KQ:
961 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
962 			EV_ADD | EV_ONESHOT, 0, 0, 0);
963 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
964 			NULL);
965 		ASSERT_LE(0, nready) << strerror(errno);
966 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
967 		if (events[0].flags & EV_ERROR)
968 			FAIL() << strerror(events[0].data);
969 		else if (events[0].flags & EV_EOF)
970 			FAIL() << strerror(events[0].fflags);
971 		m_nready = events[0].data;
972 		break;
973 	case POLL:
974 		fds[0].fd = m_fuse_fd;
975 		fds[0].events = POLLOUT;
976 		nready = poll(fds, 1, INFTIM);
977 		ASSERT_LE(0, nready) << strerror(errno);
978 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
979 		ASSERT_TRUE(fds[0].revents & POLLOUT);
980 		break;
981 	case SELECT:
982 		FD_ZERO(&writefds);
983 		FD_SET(m_fuse_fd, &writefds);
984 		nfds = m_fuse_fd + 1;
985 		nready = select(nfds, NULL, &writefds, NULL, NULL);
986 		ASSERT_LE(0, nready) << strerror(errno);
987 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
988 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
989 		break;
990 	default:
991 		FAIL() << "not yet implemented";
992 	}
993 	r = write(m_fuse_fd, &out, out.header.len);
994 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
995 }
996 
997 void* MockFS::service(void *pthr_data) {
998 	MockFS *mock_fs = (MockFS*)pthr_data;
999 
1000 	mock_fs->loop();
1001 
1002 	return (NULL);
1003 }
1004 
1005 void MockFS::unmount() {
1006 	::unmount("mountpoint", 0);
1007 }
1008