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