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