xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision b3d14eaccc5f606690d99b1998bfdf32a22404f6)
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_RENAME:
294 			{
295 				const char *src = (const char*)in.body.bytes +
296 					sizeof(fuse_rename_in);
297 				const char *dst = src + strlen(src) + 1;
298 				printf(" src=%s newdir=%" PRIu64 " dst=%s",
299 					src, in.body.rename.newdir, dst);
300 			}
301 			break;
302 		case FUSE_SETATTR:
303 			if (verbosity <= 1) {
304 				printf(" valid=%#x", in.body.setattr.valid);
305 				break;
306 			}
307 			if (in.body.setattr.valid & FATTR_MODE)
308 				printf(" mode=%#o", in.body.setattr.mode);
309 			if (in.body.setattr.valid & FATTR_UID)
310 				printf(" uid=%u", in.body.setattr.uid);
311 			if (in.body.setattr.valid & FATTR_GID)
312 				printf(" gid=%u", in.body.setattr.gid);
313 			if (in.body.setattr.valid & FATTR_SIZE)
314 				printf(" size=%" PRIu64, in.body.setattr.size);
315 			if (in.body.setattr.valid & FATTR_ATIME)
316 				printf(" atime=%" PRIu64 ".%u",
317 					in.body.setattr.atime,
318 					in.body.setattr.atimensec);
319 			if (in.body.setattr.valid & FATTR_MTIME)
320 				printf(" mtime=%" PRIu64 ".%u",
321 					in.body.setattr.mtime,
322 					in.body.setattr.mtimensec);
323 			if (in.body.setattr.valid & FATTR_FH)
324 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
325 			break;
326 		case FUSE_SETLK:
327 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
328 				" type=%u pid=%u",
329 				in.body.setlk.fh, in.body.setlk.owner,
330 				in.body.setlk.lk.type,
331 				in.body.setlk.lk.pid);
332 			if (verbosity >= 2) {
333 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
334 					in.body.setlk.lk.start,
335 					in.body.setlk.lk.end);
336 			}
337 			break;
338 		case FUSE_SETXATTR:
339 			/*
340 			 * In theory neither the xattr name and value need be
341 			 * ASCII, but in this test suite they always are.
342 			 */
343 			name = (const char*)in.body.bytes +
344 				sizeof(fuse_setxattr_in);
345 			value = name + strlen(name) + 1;
346 			printf(" %s=%s", name, value);
347 			break;
348 		case FUSE_WRITE:
349 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
350 				" size=%u write_flags=%u",
351 				in.body.write.fh,
352 				in.body.write.offset, in.body.write.size,
353 				in.body.write.write_flags);
354 			if (verbosity > 1)
355 				printf(" flags=%#x", in.body.write.flags);
356 			break;
357 		default:
358 			break;
359 	}
360 	printf("\n");
361 }
362 
363 /*
364  * Debug a FUSE response.
365  *
366  * This is mostly useful for asynchronous notifications, which don't correspond
367  * to any request
368  */
369 void MockFS::debug_response(const mockfs_buf_out &out) {
370 	const char *name;
371 
372 	if (verbosity == 0)
373 		return;
374 
375 	switch (out.header.error) {
376 		case FUSE_NOTIFY_INVAL_ENTRY:
377 			name = (const char*)out.body.bytes +
378 				sizeof(fuse_notify_inval_entry_out);
379 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
380 				out.body.inval_entry.parent, name);
381 			break;
382 		case FUSE_NOTIFY_INVAL_INODE:
383 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
384 				" len=%" PRIi64 "\n",
385 				out.body.inval_inode.ino,
386 				out.body.inval_inode.off,
387 				out.body.inval_inode.len);
388 			break;
389 		case FUSE_NOTIFY_STORE:
390 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
391 				" size=%" PRIu32 "\n",
392 				out.body.store.nodeid,
393 				out.body.store.offset,
394 				out.body.store.size);
395 			break;
396 		default:
397 			break;
398 	}
399 }
400 
401 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
402 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
403 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
404 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime)
405 {
406 	struct sigaction sa;
407 	struct iovec *iov = NULL;
408 	int iovlen = 0;
409 	char fdstr[15];
410 	const bool trueval = true;
411 
412 	m_daemon_id = NULL;
413 	m_kernel_minor_version = kernel_minor_version;
414 	m_maxreadahead = max_readahead;
415 	m_maxwrite = MIN(max_write, max_max_write);
416 	m_nready = -1;
417 	m_pm = pm;
418 	m_time_gran = time_gran;
419 	m_quit = false;
420 	m_last_unique = 0;
421 	if (m_pm == KQ)
422 		m_kq = kqueue();
423 	else
424 		m_kq = -1;
425 
426 	/*
427 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
428 	 * mkdtemp
429 	 */
430 	/*
431 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
432 	 * instead.
433 	 */
434 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
435 		throw(std::system_error(errno, std::system_category(),
436 			"Couldn't make mountpoint directory"));
437 
438 	switch (m_pm) {
439 	case BLOCKING:
440 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
441 		break;
442 	default:
443 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
444 		break;
445 	}
446 	if (m_fuse_fd < 0)
447 		throw(std::system_error(errno, std::system_category(),
448 			"Couldn't open /dev/fuse"));
449 
450 	m_pid = getpid();
451 	m_child_pid = -1;
452 
453 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
454 	build_iovec(&iov, &iovlen, "fspath",
455 		    __DECONST(void *, "mountpoint"), -1);
456 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
457 	sprintf(fdstr, "%d", m_fuse_fd);
458 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
459 	if (allow_other) {
460 		build_iovec(&iov, &iovlen, "allow_other",
461 			__DECONST(void*, &trueval), sizeof(bool));
462 	}
463 	if (default_permissions) {
464 		build_iovec(&iov, &iovlen, "default_permissions",
465 			__DECONST(void*, &trueval), sizeof(bool));
466 	}
467 	if (push_symlinks_in) {
468 		build_iovec(&iov, &iovlen, "push_symlinks_in",
469 			__DECONST(void*, &trueval), sizeof(bool));
470 	}
471 	if (ro) {
472 		build_iovec(&iov, &iovlen, "ro",
473 			__DECONST(void*, &trueval), sizeof(bool));
474 	}
475 	if (async) {
476 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
477 			sizeof(bool));
478 	}
479 	if (noatime) {
480 		build_iovec(&iov, &iovlen, "noatime",
481 			__DECONST(void*, &trueval), sizeof(bool));
482 	}
483 	if (noclusterr) {
484 		build_iovec(&iov, &iovlen, "noclusterr",
485 			__DECONST(void*, &trueval), sizeof(bool));
486 	}
487 	if (nointr) {
488 		build_iovec(&iov, &iovlen, "nointr",
489 			__DECONST(void*, &trueval), sizeof(bool));
490 	} else {
491 		build_iovec(&iov, &iovlen, "intr",
492 			__DECONST(void*, &trueval), sizeof(bool));
493 	}
494 	if (nmount(iov, iovlen, 0))
495 		throw(std::system_error(errno, std::system_category(),
496 			"Couldn't mount filesystem"));
497 
498 	// Setup default handler
499 	ON_CALL(*this, process(_, _))
500 		.WillByDefault(Invoke(this, &MockFS::process_default));
501 
502 	init(flags);
503 	bzero(&sa, sizeof(sa));
504 	sa.sa_handler = sigint_handler;
505 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
506 	if (0 != sigaction(SIGUSR1, &sa, NULL))
507 		throw(std::system_error(errno, std::system_category(),
508 			"Couldn't handle SIGUSR1"));
509 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
510 		throw(std::system_error(errno, std::system_category(),
511 			"Couldn't Couldn't start fuse thread"));
512 }
513 
514 MockFS::~MockFS() {
515 	kill_daemon();
516 	if (m_daemon_id != NULL) {
517 		pthread_join(m_daemon_id, NULL);
518 		m_daemon_id = NULL;
519 	}
520 	::unmount("mountpoint", MNT_FORCE);
521 	rmdir("mountpoint");
522 	if (m_kq >= 0)
523 		close(m_kq);
524 }
525 
526 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
527 	uint32_t inlen = in.header.len;
528 	size_t fih = sizeof(in.header);
529 	switch (in.header.opcode) {
530 	case FUSE_LOOKUP:
531 	case FUSE_RMDIR:
532 	case FUSE_SYMLINK:
533 	case FUSE_UNLINK:
534 		EXPECT_GT(inlen, fih) << "Missing request filename";
535 		// No redundant information for checking buflen
536 		break;
537 	case FUSE_FORGET:
538 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
539 		EXPECT_EQ((size_t)buflen, inlen);
540 		break;
541 	case FUSE_GETATTR:
542 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
543 		EXPECT_EQ((size_t)buflen, inlen);
544 		break;
545 	case FUSE_SETATTR:
546 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
547 		EXPECT_EQ((size_t)buflen, inlen);
548 		break;
549 	case FUSE_READLINK:
550 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
551 		EXPECT_EQ((size_t)buflen, inlen);
552 		break;
553 	case FUSE_MKNOD:
554 		{
555 			size_t s;
556 			if (m_kernel_minor_version >= 12)
557 				s = sizeof(in.body.mknod);
558 			else
559 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
560 			EXPECT_GE(inlen, fih + s) << "Missing request body";
561 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
562 			// No redundant information for checking buflen
563 			break;
564 		}
565 	case FUSE_MKDIR:
566 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
567 			"Missing request body";
568 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
569 			"Missing request filename";
570 		// No redundant information for checking buflen
571 		break;
572 	case FUSE_RENAME:
573 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
574 			"Missing request body";
575 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
576 			"Missing request filename";
577 		// No redundant information for checking buflen
578 		break;
579 	case FUSE_LINK:
580 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
581 			"Missing request body";
582 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
583 			"Missing request filename";
584 		// No redundant information for checking buflen
585 		break;
586 	case FUSE_OPEN:
587 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
588 		EXPECT_EQ((size_t)buflen, inlen);
589 		break;
590 	case FUSE_READ:
591 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
592 		EXPECT_EQ((size_t)buflen, inlen);
593 		break;
594 	case FUSE_WRITE:
595 		{
596 			size_t s;
597 
598 			if (m_kernel_minor_version >= 9)
599 				s = sizeof(in.body.write);
600 			else
601 				s = FUSE_COMPAT_WRITE_IN_SIZE;
602 			// I suppose a 0-byte write should be allowed
603 			EXPECT_GE(inlen, fih + s) << "Missing request body";
604 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
605 			break;
606 		}
607 	case FUSE_DESTROY:
608 	case FUSE_STATFS:
609 		EXPECT_EQ(inlen, fih);
610 		EXPECT_EQ((size_t)buflen, inlen);
611 		break;
612 	case FUSE_RELEASE:
613 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
614 		EXPECT_EQ((size_t)buflen, inlen);
615 		break;
616 	case FUSE_FSYNC:
617 	case FUSE_FSYNCDIR:
618 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
619 		EXPECT_EQ((size_t)buflen, inlen);
620 		break;
621 	case FUSE_SETXATTR:
622 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
623 			"Missing request body";
624 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
625 			"Missing request attribute name";
626 		// No redundant information for checking buflen
627 		break;
628 	case FUSE_GETXATTR:
629 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
630 			"Missing request body";
631 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
632 			"Missing request attribute name";
633 		// No redundant information for checking buflen
634 		break;
635 	case FUSE_LISTXATTR:
636 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
637 		EXPECT_EQ((size_t)buflen, inlen);
638 		break;
639 	case FUSE_REMOVEXATTR:
640 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
641 		// No redundant information for checking buflen
642 		break;
643 	case FUSE_FLUSH:
644 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
645 		EXPECT_EQ((size_t)buflen, inlen);
646 		break;
647 	case FUSE_INIT:
648 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
649 		EXPECT_EQ((size_t)buflen, inlen);
650 		break;
651 	case FUSE_OPENDIR:
652 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
653 		EXPECT_EQ((size_t)buflen, inlen);
654 		break;
655 	case FUSE_READDIR:
656 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
657 		EXPECT_EQ((size_t)buflen, inlen);
658 		break;
659 	case FUSE_RELEASEDIR:
660 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
661 		EXPECT_EQ((size_t)buflen, inlen);
662 		break;
663 	case FUSE_GETLK:
664 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
665 		EXPECT_EQ((size_t)buflen, inlen);
666 		break;
667 	case FUSE_SETLK:
668 	case FUSE_SETLKW:
669 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
670 		EXPECT_EQ((size_t)buflen, inlen);
671 		break;
672 	case FUSE_ACCESS:
673 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
674 		EXPECT_EQ((size_t)buflen, inlen);
675 		break;
676 	case FUSE_CREATE:
677 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
678 			"Missing request body";
679 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
680 			"Missing request filename";
681 		// No redundant information for checking buflen
682 		break;
683 	case FUSE_INTERRUPT:
684 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
685 		EXPECT_EQ((size_t)buflen, inlen);
686 		break;
687 	case FUSE_BMAP:
688 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
689 		EXPECT_EQ((size_t)buflen, inlen);
690 		break;
691 	case FUSE_LSEEK:
692 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
693 		EXPECT_EQ((size_t)buflen, inlen);
694 		break;
695 	case FUSE_COPY_FILE_RANGE:
696 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
697 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
698 		EXPECT_EQ((size_t)buflen, inlen);
699 		break;
700 	case FUSE_NOTIFY_REPLY:
701 	case FUSE_BATCH_FORGET:
702 	case FUSE_FALLOCATE:
703 	case FUSE_IOCTL:
704 	case FUSE_POLL:
705 	case FUSE_READDIRPLUS:
706 		FAIL() << "Unsupported opcode?";
707 	default:
708 		FAIL() << "Unknown opcode " << in.header.opcode;
709 	}
710 	/*
711 	 * Check that the ticket's unique value is sequential.  Technically it
712 	 * doesn't need to be sequential, merely unique.  But the current
713 	 * fusefs driver _does_ make it sequential, and that's easy to check
714 	 * for.
715 	 */
716 	if (in.header.unique != ++m_last_unique)
717 		FAIL() << "Non-sequential unique value";
718 }
719 
720 void MockFS::init(uint32_t flags) {
721 	ssize_t buflen;
722 
723 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
724 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
725 
726 	read_request(*in, buflen);
727 	if (verbosity > 0)
728 		debug_request(*in, buflen);
729 	audit_request(*in, buflen);
730 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
731 
732 	out->header.unique = in->header.unique;
733 	out->header.error = 0;
734 	out->body.init.major = FUSE_KERNEL_VERSION;
735 	out->body.init.minor = m_kernel_minor_version;;
736 	out->body.init.flags = in->body.init.flags & flags;
737 	out->body.init.max_write = m_maxwrite;
738 	out->body.init.max_readahead = m_maxreadahead;
739 
740 	if (m_kernel_minor_version < 23) {
741 		SET_OUT_HEADER_LEN(*out, init_7_22);
742 	} else {
743 		out->body.init.time_gran = m_time_gran;
744 		SET_OUT_HEADER_LEN(*out, init);
745 	}
746 
747 	write(m_fuse_fd, out.get(), out->header.len);
748 }
749 
750 void MockFS::kill_daemon() {
751 	m_quit = true;
752 	if (m_daemon_id != NULL)
753 		pthread_kill(m_daemon_id, SIGUSR1);
754 	// Closing the /dev/fuse file descriptor first allows unmount to
755 	// succeed even if the daemon doesn't correctly respond to commands
756 	// during the unmount sequence.
757 	close(m_fuse_fd);
758 	m_fuse_fd = -1;
759 }
760 
761 void MockFS::loop() {
762 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
763 
764 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
765 	ASSERT_TRUE(in != NULL);
766 	while (!m_quit) {
767 		ssize_t buflen;
768 
769 		bzero(in.get(), sizeof(*in));
770 		read_request(*in, buflen);
771 		if (m_quit)
772 			break;
773 		if (verbosity > 0)
774 			debug_request(*in, buflen);
775 		audit_request(*in, buflen);
776 		if (pid_ok((pid_t)in->header.pid)) {
777 			process(*in, out);
778 		} else {
779 			/*
780 			 * Reject any requests from unknown processes.  Because
781 			 * we actually do mount a filesystem, plenty of
782 			 * unrelated system daemons may try to access it.
783 			 */
784 			if (verbosity > 1)
785 				printf("\tREJECTED (wrong pid %d)\n",
786 					in->header.pid);
787 			process_default(*in, out);
788 		}
789 		for (auto &it: out)
790 			write_response(*it);
791 		out.clear();
792 	}
793 }
794 
795 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
796 {
797 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
798 
799 	out->header.unique = 0;	/* 0 means asynchronous notification */
800 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
801 	out->body.inval_entry.parent = parent;
802 	out->body.inval_entry.namelen = namelen;
803 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
804 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
805 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
806 		namelen;
807 	debug_response(*out);
808 	write_response(*out);
809 	return 0;
810 }
811 
812 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
813 {
814 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
815 
816 	out->header.unique = 0;	/* 0 means asynchronous notification */
817 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
818 	out->body.inval_inode.ino = ino;
819 	out->body.inval_inode.off = off;
820 	out->body.inval_inode.len = len;
821 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
822 	debug_response(*out);
823 	write_response(*out);
824 	return 0;
825 }
826 
827 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
828 {
829 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
830 
831 	out->header.unique = 0;	/* 0 means asynchronous notification */
832 	out->header.error = FUSE_NOTIFY_STORE;
833 	out->body.store.nodeid = ino;
834 	out->body.store.offset = off;
835 	out->body.store.size = size;
836 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
837 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
838 	debug_response(*out);
839 	write_response(*out);
840 	return 0;
841 }
842 
843 bool MockFS::pid_ok(pid_t pid) {
844 	if (pid == m_pid) {
845 		return (true);
846 	} else if (pid == m_child_pid) {
847 		return (true);
848 	} else {
849 		struct kinfo_proc *ki;
850 		bool ok = false;
851 
852 		ki = kinfo_getproc(pid);
853 		if (ki == NULL)
854 			return (false);
855 		/*
856 		 * Allow access by the aio daemon processes so that our tests
857 		 * can use aio functions
858 		 */
859 		if (0 == strncmp("aiod", ki->ki_comm, 4))
860 			ok = true;
861 		free(ki);
862 		return (ok);
863 	}
864 }
865 
866 void MockFS::process_default(const mockfs_buf_in& in,
867 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
868 {
869 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
870 	out0->header.unique = in.header.unique;
871 	out0->header.error = -EOPNOTSUPP;
872 	out0->header.len = sizeof(out0->header);
873 	out.push_back(std::move(out0));
874 }
875 
876 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
877 	int nready = 0;
878 	fd_set readfds;
879 	pollfd fds[1];
880 	struct kevent changes[1];
881 	struct kevent events[1];
882 	struct timespec timeout_ts;
883 	struct timeval timeout_tv;
884 	const int timeout_ms = 999;
885 	int timeout_int, nfds;
886 	int fuse_fd;
887 
888 	switch (m_pm) {
889 	case BLOCKING:
890 		break;
891 	case KQ:
892 		timeout_ts.tv_sec = 0;
893 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
894 		while (nready == 0) {
895 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
896 				EV_ADD | EV_ONESHOT, 0, 0, 0);
897 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
898 				&timeout_ts);
899 			if (m_quit)
900 				return;
901 		}
902 		ASSERT_LE(0, nready) << strerror(errno);
903 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
904 		if (events[0].flags & EV_ERROR)
905 			FAIL() << strerror(events[0].data);
906 		else if (events[0].flags & EV_EOF)
907 			FAIL() << strerror(events[0].fflags);
908 		m_nready = events[0].data;
909 		break;
910 	case POLL:
911 		timeout_int = timeout_ms;
912 		fds[0].fd = m_fuse_fd;
913 		fds[0].events = POLLIN;
914 		while (nready == 0) {
915 			nready = poll(fds, 1, timeout_int);
916 			if (m_quit)
917 				return;
918 		}
919 		ASSERT_LE(0, nready) << strerror(errno);
920 		ASSERT_TRUE(fds[0].revents & POLLIN);
921 		break;
922 	case SELECT:
923 		fuse_fd = m_fuse_fd;
924 		if (fuse_fd < 0)
925 			break;
926 		timeout_tv.tv_sec = 0;
927 		timeout_tv.tv_usec = timeout_ms * 1'000;
928 		nfds = fuse_fd + 1;
929 		while (nready == 0) {
930 			FD_ZERO(&readfds);
931 			FD_SET(fuse_fd, &readfds);
932 			nready = select(nfds, &readfds, NULL, NULL,
933 				&timeout_tv);
934 			if (m_quit)
935 				return;
936 		}
937 		ASSERT_LE(0, nready) << strerror(errno);
938 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
939 		break;
940 	default:
941 		FAIL() << "not yet implemented";
942 	}
943 	res = read(m_fuse_fd, &in, sizeof(in));
944 
945 	if (res < 0 && !m_quit) {
946 		m_quit = true;
947 		FAIL() << "read: " << strerror(errno);
948 	}
949 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
950 	/*
951 	 * Inconsistently, fuse_in_header.len is the size of the entire
952 	 * request,including header, even though fuse_out_header.len excludes
953 	 * the size of the header.
954 	 */
955 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
956 }
957 
958 void MockFS::write_response(const mockfs_buf_out &out) {
959 	fd_set writefds;
960 	pollfd fds[1];
961 	struct kevent changes[1];
962 	struct kevent events[1];
963 	int nready, nfds;
964 	ssize_t r;
965 
966 	switch (m_pm) {
967 	case BLOCKING:
968 		break;
969 	case KQ:
970 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
971 			EV_ADD | EV_ONESHOT, 0, 0, 0);
972 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
973 			NULL);
974 		ASSERT_LE(0, nready) << strerror(errno);
975 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
976 		if (events[0].flags & EV_ERROR)
977 			FAIL() << strerror(events[0].data);
978 		else if (events[0].flags & EV_EOF)
979 			FAIL() << strerror(events[0].fflags);
980 		m_nready = events[0].data;
981 		break;
982 	case POLL:
983 		fds[0].fd = m_fuse_fd;
984 		fds[0].events = POLLOUT;
985 		nready = poll(fds, 1, INFTIM);
986 		ASSERT_LE(0, nready) << strerror(errno);
987 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
988 		ASSERT_TRUE(fds[0].revents & POLLOUT);
989 		break;
990 	case SELECT:
991 		FD_ZERO(&writefds);
992 		FD_SET(m_fuse_fd, &writefds);
993 		nfds = m_fuse_fd + 1;
994 		nready = select(nfds, NULL, &writefds, NULL, NULL);
995 		ASSERT_LE(0, nready) << strerror(errno);
996 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
997 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
998 		break;
999 	default:
1000 		FAIL() << "not yet implemented";
1001 	}
1002 	r = write(m_fuse_fd, &out, out.header.len);
1003 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1004 }
1005 
1006 void* MockFS::service(void *pthr_data) {
1007 	MockFS *mock_fs = (MockFS*)pthr_data;
1008 
1009 	mock_fs->loop();
1010 
1011 	return (NULL);
1012 }
1013 
1014 void MockFS::unmount() {
1015 	::unmount("mountpoint", 0);
1016 }
1017