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