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