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