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