xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 2a58b312)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 The FreeBSD Foundation
5  *
6  * This software was developed by BFF Storage Systems, LLC under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32 
33 extern "C" {
34 #include <sys/param.h>
35 
36 #include <sys/mount.h>
37 #include <sys/select.h>
38 #include <sys/stat.h>
39 #include <sys/uio.h>
40 #include <sys/user.h>
41 
42 #include <fcntl.h>
43 #include <libutil.h>
44 #include <poll.h>
45 #include <pthread.h>
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 
50 #include "mntopts.h"	// for build_iovec
51 }
52 
53 #include <cinttypes>
54 
55 #include <gtest/gtest.h>
56 
57 #include "mockfs.hh"
58 
59 using namespace testing;
60 
61 int verbosity = 0;
62 
63 const char* opcode2opname(uint32_t opcode)
64 {
65 	const char* table[] = {
66 		"Unknown (opcode 0)",
67 		"LOOKUP",
68 		"FORGET",
69 		"GETATTR",
70 		"SETATTR",
71 		"READLINK",
72 		"SYMLINK",
73 		"Unknown (opcode 7)",
74 		"MKNOD",
75 		"MKDIR",
76 		"UNLINK",
77 		"RMDIR",
78 		"RENAME",
79 		"LINK",
80 		"OPEN",
81 		"READ",
82 		"WRITE",
83 		"STATFS",
84 		"RELEASE",
85 		"Unknown (opcode 19)",
86 		"FSYNC",
87 		"SETXATTR",
88 		"GETXATTR",
89 		"LISTXATTR",
90 		"REMOVEXATTR",
91 		"FLUSH",
92 		"INIT",
93 		"OPENDIR",
94 		"READDIR",
95 		"RELEASEDIR",
96 		"FSYNCDIR",
97 		"GETLK",
98 		"SETLK",
99 		"SETLKW",
100 		"ACCESS",
101 		"CREATE",
102 		"INTERRUPT",
103 		"BMAP",
104 		"DESTROY",
105 		"IOCTL",
106 		"POLL",
107 		"NOTIFY_REPLY",
108 		"BATCH_FORGET",
109 		"FALLOCATE",
110 		"READDIRPLUS",
111 		"RENAME2",
112 		"LSEEK",
113 		"COPY_FILE_RANGE",
114 	};
115 	if (opcode >= nitems(table))
116 		return ("Unknown (opcode > max)");
117 	else
118 		return (table[opcode]);
119 }
120 
121 ProcessMockerT
122 ReturnErrno(int error)
123 {
124 	return([=](auto in, auto &out) {
125 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
126 		out0->header.unique = in.header.unique;
127 		out0->header.error = -error;
128 		out0->header.len = sizeof(out0->header);
129 		out.push_back(std::move(out0));
130 	});
131 }
132 
133 /* Helper function used for returning negative cache entries for LOOKUP */
134 ProcessMockerT
135 ReturnNegativeCache(const struct timespec *entry_valid)
136 {
137 	return([=](auto in, auto &out) {
138 		/* nodeid means ENOENT and cache it */
139 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
140 		out0->body.entry.nodeid = 0;
141 		out0->header.unique = in.header.unique;
142 		out0->header.error = 0;
143 		out0->body.entry.entry_valid = entry_valid->tv_sec;
144 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
145 		SET_OUT_HEADER_LEN(*out0, entry);
146 		out.push_back(std::move(out0));
147 	});
148 }
149 
150 ProcessMockerT
151 ReturnImmediate(std::function<void(const mockfs_buf_in& in,
152 				   struct mockfs_buf_out &out)> f)
153 {
154 	return([=](auto& in, auto &out) {
155 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
156 		out0->header.unique = in.header.unique;
157 		f(in, *out0);
158 		out.push_back(std::move(out0));
159 	});
160 }
161 
162 void sigint_handler(int __unused sig) {
163 	// Don't do anything except interrupt the daemon's read(2) call
164 }
165 
166 void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
167 {
168 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
169 		in.header.nodeid);
170 	if (verbosity > 1) {
171 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
172 			" buflen=%zd",
173 			in.header.uid, in.header.gid, in.header.pid,
174 			in.header.unique, in.header.len, buflen);
175 	}
176 	switch (in.header.opcode) {
177 		const char *name, *value;
178 
179 		case FUSE_ACCESS:
180 			printf(" mask=%#x", in.body.access.mask);
181 			break;
182 		case FUSE_BMAP:
183 			printf(" block=%" PRIx64 " blocksize=%#x",
184 				in.body.bmap.block, in.body.bmap.blocksize);
185 			break;
186 		case FUSE_COPY_FILE_RANGE:
187 			printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
188 			       " off_out=%" PRIu64 " size=%" PRIu64,
189 			       in.body.copy_file_range.off_in,
190 			       in.body.copy_file_range.nodeid_out,
191 			       in.body.copy_file_range.off_out,
192 			       in.body.copy_file_range.len);
193 			if (verbosity > 1)
194 				printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
195 				       " flags=%" PRIx64,
196 				       in.body.copy_file_range.fh_in,
197 				       in.body.copy_file_range.fh_out,
198 				       in.body.copy_file_range.flags);
199 			break;
200 		case FUSE_CREATE:
201 			if (m_kernel_minor_version >= 12)
202 				name = (const char*)in.body.bytes +
203 					sizeof(fuse_create_in);
204 			else
205 				name = (const char*)in.body.bytes +
206 					sizeof(fuse_open_in);
207 			printf(" flags=%#x name=%s",
208 				in.body.open.flags, name);
209 			break;
210 		case FUSE_FALLOCATE:
211 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
212 				" length=%" PRIx64 " mode=%#x",
213 				in.body.fallocate.fh,
214 				in.body.fallocate.offset,
215 				in.body.fallocate.length,
216 				in.body.fallocate.mode);
217 			break;
218 		case FUSE_FLUSH:
219 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
220 				in.body.flush.fh,
221 				in.body.flush.lock_owner);
222 			break;
223 		case FUSE_FORGET:
224 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
225 			break;
226 		case FUSE_FSYNC:
227 			printf(" flags=%#x", in.body.fsync.fsync_flags);
228 			break;
229 		case FUSE_FSYNCDIR:
230 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
231 			break;
232 		case FUSE_GETLK:
233 			printf(" fh=%#" PRIx64
234 				" type=%u pid=%u",
235 				in.body.getlk.fh,
236 				in.body.getlk.lk.type,
237 				in.body.getlk.lk.pid);
238 			if (verbosity >= 2) {
239 				printf(" range=[%" PRIi64 ":%" PRIi64 "]",
240 					in.body.getlk.lk.start,
241 					in.body.getlk.lk.end);
242 			}
243 			break;
244 		case FUSE_INTERRUPT:
245 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
246 			break;
247 		case FUSE_LINK:
248 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
249 			break;
250 		case FUSE_LISTXATTR:
251 			printf(" size=%" PRIu32, in.body.listxattr.size);
252 			break;
253 		case FUSE_LOOKUP:
254 			printf(" %s", in.body.lookup);
255 			break;
256 		case FUSE_LSEEK:
257 			switch (in.body.lseek.whence) {
258 			case SEEK_HOLE:
259 				printf(" SEEK_HOLE offset=%jd",
260 				    in.body.lseek.offset);
261 				break;
262 			case SEEK_DATA:
263 				printf(" SEEK_DATA offset=%jd",
264 				    in.body.lseek.offset);
265 				break;
266 			default:
267 				printf(" whence=%u offset=%jd",
268 				    in.body.lseek.whence, in.body.lseek.offset);
269 				break;
270 			}
271 			break;
272 		case FUSE_MKDIR:
273 			name = (const char*)in.body.bytes +
274 				sizeof(fuse_mkdir_in);
275 			printf(" name=%s mode=%#o umask=%#o", name,
276 				in.body.mkdir.mode, in.body.mkdir.umask);
277 			break;
278 		case FUSE_MKNOD:
279 			if (m_kernel_minor_version >= 12)
280 				name = (const char*)in.body.bytes +
281 					sizeof(fuse_mknod_in);
282 			else
283 				name = (const char*)in.body.bytes +
284 					FUSE_COMPAT_MKNOD_IN_SIZE;
285 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
286 				in.body.mknod.mode, in.body.mknod.rdev,
287 				in.body.mknod.umask, name);
288 			break;
289 		case FUSE_OPEN:
290 			printf(" flags=%#x", in.body.open.flags);
291 			break;
292 		case FUSE_OPENDIR:
293 			printf(" flags=%#x", in.body.opendir.flags);
294 			break;
295 		case FUSE_READ:
296 			printf(" offset=%" PRIu64 " size=%u",
297 				in.body.read.offset,
298 				in.body.read.size);
299 			if (verbosity > 1)
300 				printf(" flags=%#x", in.body.read.flags);
301 			break;
302 		case FUSE_READDIR:
303 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
304 				in.body.readdir.fh, in.body.readdir.offset,
305 				in.body.readdir.size);
306 			break;
307 		case FUSE_RELEASE:
308 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
309 				in.body.release.fh,
310 				in.body.release.flags,
311 				in.body.release.lock_owner);
312 			break;
313 		case FUSE_RENAME:
314 			{
315 				const char *src = (const char*)in.body.bytes +
316 					sizeof(fuse_rename_in);
317 				const char *dst = src + strlen(src) + 1;
318 				printf(" src=%s newdir=%" PRIu64 " dst=%s",
319 					src, in.body.rename.newdir, dst);
320 			}
321 			break;
322 		case FUSE_SETATTR:
323 			if (verbosity <= 1) {
324 				printf(" valid=%#x", in.body.setattr.valid);
325 				break;
326 			}
327 			if (in.body.setattr.valid & FATTR_MODE)
328 				printf(" mode=%#o", in.body.setattr.mode);
329 			if (in.body.setattr.valid & FATTR_UID)
330 				printf(" uid=%u", in.body.setattr.uid);
331 			if (in.body.setattr.valid & FATTR_GID)
332 				printf(" gid=%u", in.body.setattr.gid);
333 			if (in.body.setattr.valid & FATTR_SIZE)
334 				printf(" size=%" PRIu64, in.body.setattr.size);
335 			if (in.body.setattr.valid & FATTR_ATIME)
336 				printf(" atime=%" PRIu64 ".%u",
337 					in.body.setattr.atime,
338 					in.body.setattr.atimensec);
339 			if (in.body.setattr.valid & FATTR_MTIME)
340 				printf(" mtime=%" PRIu64 ".%u",
341 					in.body.setattr.mtime,
342 					in.body.setattr.mtimensec);
343 			if (in.body.setattr.valid & FATTR_FH)
344 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
345 			break;
346 		case FUSE_SETLK:
347 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
348 				" type=%u pid=%u",
349 				in.body.setlk.fh, in.body.setlk.owner,
350 				in.body.setlk.lk.type,
351 				in.body.setlk.lk.pid);
352 			if (verbosity >= 2) {
353 				printf(" range=[%" PRIi64 ":%" PRIi64 "]",
354 					in.body.setlk.lk.start,
355 					in.body.setlk.lk.end);
356 			}
357 			break;
358 		case FUSE_SETXATTR:
359 			/*
360 			 * In theory neither the xattr name and value need be
361 			 * ASCII, but in this test suite they always are.
362 			 */
363 			name = (const char*)in.body.bytes +
364 				sizeof(fuse_setxattr_in);
365 			value = name + strlen(name) + 1;
366 			printf(" %s=%s", name, value);
367 			break;
368 		case FUSE_WRITE:
369 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
370 				" size=%u write_flags=%u",
371 				in.body.write.fh,
372 				in.body.write.offset, in.body.write.size,
373 				in.body.write.write_flags);
374 			if (verbosity > 1)
375 				printf(" flags=%#x", in.body.write.flags);
376 			break;
377 		default:
378 			break;
379 	}
380 	printf("\n");
381 }
382 
383 /*
384  * Debug a FUSE response.
385  *
386  * This is mostly useful for asynchronous notifications, which don't correspond
387  * to any request
388  */
389 void MockFS::debug_response(const mockfs_buf_out &out) {
390 	const char *name;
391 
392 	if (verbosity == 0)
393 		return;
394 
395 	switch (out.header.error) {
396 		case FUSE_NOTIFY_INVAL_ENTRY:
397 			name = (const char*)out.body.bytes +
398 				sizeof(fuse_notify_inval_entry_out);
399 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
400 				out.body.inval_entry.parent, name);
401 			break;
402 		case FUSE_NOTIFY_INVAL_INODE:
403 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
404 				" len=%" PRIi64 "\n",
405 				out.body.inval_inode.ino,
406 				out.body.inval_inode.off,
407 				out.body.inval_inode.len);
408 			break;
409 		case FUSE_NOTIFY_STORE:
410 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
411 				" size=%" PRIu32 "\n",
412 				out.body.store.nodeid,
413 				out.body.store.offset,
414 				out.body.store.size);
415 			break;
416 		default:
417 			break;
418 	}
419 }
420 
421 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
422 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
423 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
424 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
425 	const char *fsname, const char *subtype)
426 {
427 	struct sigaction sa;
428 	struct iovec *iov = NULL;
429 	int iovlen = 0;
430 	char fdstr[15];
431 	const bool trueval = true;
432 
433 	m_daemon_id = NULL;
434 	m_kernel_minor_version = kernel_minor_version;
435 	m_maxreadahead = max_readahead;
436 	m_maxwrite = MIN(max_write, max_max_write);
437 	m_nready = -1;
438 	m_pm = pm;
439 	m_time_gran = time_gran;
440 	m_quit = false;
441 	m_last_unique = 0;
442 	if (m_pm == KQ)
443 		m_kq = kqueue();
444 	else
445 		m_kq = -1;
446 
447 	/*
448 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
449 	 * mkdtemp
450 	 */
451 	/*
452 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
453 	 * instead.
454 	 */
455 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
456 		throw(std::system_error(errno, std::system_category(),
457 			"Couldn't make mountpoint directory"));
458 
459 	switch (m_pm) {
460 	case BLOCKING:
461 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
462 		break;
463 	default:
464 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
465 		break;
466 	}
467 	if (m_fuse_fd < 0)
468 		throw(std::system_error(errno, std::system_category(),
469 			"Couldn't open /dev/fuse"));
470 
471 	m_pid = getpid();
472 	m_child_pid = -1;
473 
474 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
475 	build_iovec(&iov, &iovlen, "fspath",
476 		    __DECONST(void *, "mountpoint"), -1);
477 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
478 	sprintf(fdstr, "%d", m_fuse_fd);
479 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
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 
527 	// Setup default handler
528 	ON_CALL(*this, process(_, _))
529 		.WillByDefault(Invoke(this, &MockFS::process_default));
530 
531 	init(flags);
532 	bzero(&sa, sizeof(sa));
533 	sa.sa_handler = sigint_handler;
534 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
535 	if (0 != sigaction(SIGUSR1, &sa, NULL))
536 		throw(std::system_error(errno, std::system_category(),
537 			"Couldn't handle SIGUSR1"));
538 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
539 		throw(std::system_error(errno, std::system_category(),
540 			"Couldn't Couldn't start fuse thread"));
541 }
542 
543 MockFS::~MockFS() {
544 	kill_daemon();
545 	if (m_daemon_id != NULL) {
546 		pthread_join(m_daemon_id, NULL);
547 		m_daemon_id = NULL;
548 	}
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 	/*
743 	 * Check that the ticket's unique value is sequential.  Technically it
744 	 * doesn't need to be sequential, merely unique.  But the current
745 	 * fusefs driver _does_ make it sequential, and that's easy to check
746 	 * for.
747 	 */
748 	if (in.header.unique != ++m_last_unique)
749 		FAIL() << "Non-sequential unique value";
750 }
751 
752 void MockFS::init(uint32_t flags) {
753 	ssize_t buflen;
754 
755 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
756 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
757 
758 	read_request(*in, buflen);
759 	if (verbosity > 0)
760 		debug_request(*in, buflen);
761 	audit_request(*in, buflen);
762 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
763 
764 	out->header.unique = in->header.unique;
765 	out->header.error = 0;
766 	out->body.init.major = FUSE_KERNEL_VERSION;
767 	out->body.init.minor = m_kernel_minor_version;;
768 	out->body.init.flags = in->body.init.flags & flags;
769 	out->body.init.max_write = m_maxwrite;
770 	out->body.init.max_readahead = m_maxreadahead;
771 
772 	if (m_kernel_minor_version < 23) {
773 		SET_OUT_HEADER_LEN(*out, init_7_22);
774 	} else {
775 		out->body.init.time_gran = m_time_gran;
776 		SET_OUT_HEADER_LEN(*out, init);
777 	}
778 
779 	write(m_fuse_fd, out.get(), out->header.len);
780 }
781 
782 void MockFS::kill_daemon() {
783 	m_quit = true;
784 	if (m_daemon_id != NULL)
785 		pthread_kill(m_daemon_id, SIGUSR1);
786 	// Closing the /dev/fuse file descriptor first allows unmount to
787 	// succeed even if the daemon doesn't correctly respond to commands
788 	// during the unmount sequence.
789 	close(m_fuse_fd);
790 	m_fuse_fd = -1;
791 }
792 
793 void MockFS::loop() {
794 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
795 
796 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
797 	ASSERT_TRUE(in != NULL);
798 	while (!m_quit) {
799 		ssize_t buflen;
800 
801 		bzero(in.get(), sizeof(*in));
802 		read_request(*in, buflen);
803 		if (m_quit)
804 			break;
805 		if (verbosity > 0)
806 			debug_request(*in, buflen);
807 		audit_request(*in, buflen);
808 		if (pid_ok((pid_t)in->header.pid)) {
809 			process(*in, out);
810 		} else {
811 			/*
812 			 * Reject any requests from unknown processes.  Because
813 			 * we actually do mount a filesystem, plenty of
814 			 * unrelated system daemons may try to access it.
815 			 */
816 			if (verbosity > 1)
817 				printf("\tREJECTED (wrong pid %d)\n",
818 					in->header.pid);
819 			process_default(*in, out);
820 		}
821 		for (auto &it: out)
822 			write_response(*it);
823 		out.clear();
824 	}
825 }
826 
827 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
828 {
829 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
830 
831 	out->header.unique = 0;	/* 0 means asynchronous notification */
832 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
833 	out->body.inval_entry.parent = parent;
834 	out->body.inval_entry.namelen = namelen;
835 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
836 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
837 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
838 		namelen;
839 	debug_response(*out);
840 	write_response(*out);
841 	return 0;
842 }
843 
844 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
845 {
846 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
847 
848 	out->header.unique = 0;	/* 0 means asynchronous notification */
849 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
850 	out->body.inval_inode.ino = ino;
851 	out->body.inval_inode.off = off;
852 	out->body.inval_inode.len = len;
853 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
854 	debug_response(*out);
855 	write_response(*out);
856 	return 0;
857 }
858 
859 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
860 {
861 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
862 
863 	out->header.unique = 0;	/* 0 means asynchronous notification */
864 	out->header.error = FUSE_NOTIFY_STORE;
865 	out->body.store.nodeid = ino;
866 	out->body.store.offset = off;
867 	out->body.store.size = size;
868 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
869 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
870 	debug_response(*out);
871 	write_response(*out);
872 	return 0;
873 }
874 
875 bool MockFS::pid_ok(pid_t pid) {
876 	if (pid == m_pid) {
877 		return (true);
878 	} else if (pid == m_child_pid) {
879 		return (true);
880 	} else {
881 		struct kinfo_proc *ki;
882 		bool ok = false;
883 
884 		ki = kinfo_getproc(pid);
885 		if (ki == NULL)
886 			return (false);
887 		/*
888 		 * Allow access by the aio daemon processes so that our tests
889 		 * can use aio functions
890 		 */
891 		if (0 == strncmp("aiod", ki->ki_comm, 4))
892 			ok = true;
893 		free(ki);
894 		return (ok);
895 	}
896 }
897 
898 void MockFS::process_default(const mockfs_buf_in& in,
899 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
900 {
901 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
902 	out0->header.unique = in.header.unique;
903 	out0->header.error = -EOPNOTSUPP;
904 	out0->header.len = sizeof(out0->header);
905 	out.push_back(std::move(out0));
906 }
907 
908 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
909 	int nready = 0;
910 	fd_set readfds;
911 	pollfd fds[1];
912 	struct kevent changes[1];
913 	struct kevent events[1];
914 	struct timespec timeout_ts;
915 	struct timeval timeout_tv;
916 	const int timeout_ms = 999;
917 	int timeout_int, nfds;
918 	int fuse_fd;
919 
920 	switch (m_pm) {
921 	case BLOCKING:
922 		break;
923 	case KQ:
924 		timeout_ts.tv_sec = 0;
925 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
926 		while (nready == 0) {
927 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
928 				EV_ADD | EV_ONESHOT, 0, 0, 0);
929 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
930 				&timeout_ts);
931 			if (m_quit)
932 				return;
933 		}
934 		ASSERT_LE(0, nready) << strerror(errno);
935 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
936 		if (events[0].flags & EV_ERROR)
937 			FAIL() << strerror(events[0].data);
938 		else if (events[0].flags & EV_EOF)
939 			FAIL() << strerror(events[0].fflags);
940 		m_nready = events[0].data;
941 		break;
942 	case POLL:
943 		timeout_int = timeout_ms;
944 		fds[0].fd = m_fuse_fd;
945 		fds[0].events = POLLIN;
946 		while (nready == 0) {
947 			nready = poll(fds, 1, timeout_int);
948 			if (m_quit)
949 				return;
950 		}
951 		ASSERT_LE(0, nready) << strerror(errno);
952 		ASSERT_TRUE(fds[0].revents & POLLIN);
953 		break;
954 	case SELECT:
955 		fuse_fd = m_fuse_fd;
956 		if (fuse_fd < 0)
957 			break;
958 		timeout_tv.tv_sec = 0;
959 		timeout_tv.tv_usec = timeout_ms * 1'000;
960 		nfds = fuse_fd + 1;
961 		while (nready == 0) {
962 			FD_ZERO(&readfds);
963 			FD_SET(fuse_fd, &readfds);
964 			nready = select(nfds, &readfds, NULL, NULL,
965 				&timeout_tv);
966 			if (m_quit)
967 				return;
968 		}
969 		ASSERT_LE(0, nready) << strerror(errno);
970 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
971 		break;
972 	default:
973 		FAIL() << "not yet implemented";
974 	}
975 	res = read(m_fuse_fd, &in, sizeof(in));
976 
977 	if (res < 0 && !m_quit) {
978 		m_quit = true;
979 		FAIL() << "read: " << strerror(errno);
980 	}
981 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
982 	/*
983 	 * Inconsistently, fuse_in_header.len is the size of the entire
984 	 * request,including header, even though fuse_out_header.len excludes
985 	 * the size of the header.
986 	 */
987 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
988 }
989 
990 void MockFS::write_response(const mockfs_buf_out &out) {
991 	fd_set writefds;
992 	pollfd fds[1];
993 	struct kevent changes[1];
994 	struct kevent events[1];
995 	int nready, nfds;
996 	ssize_t r;
997 
998 	switch (m_pm) {
999 	case BLOCKING:
1000 		break;
1001 	case KQ:
1002 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1003 			EV_ADD | EV_ONESHOT, 0, 0, 0);
1004 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1005 			NULL);
1006 		ASSERT_LE(0, nready) << strerror(errno);
1007 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1008 		if (events[0].flags & EV_ERROR)
1009 			FAIL() << strerror(events[0].data);
1010 		else if (events[0].flags & EV_EOF)
1011 			FAIL() << strerror(events[0].fflags);
1012 		m_nready = events[0].data;
1013 		break;
1014 	case POLL:
1015 		fds[0].fd = m_fuse_fd;
1016 		fds[0].events = POLLOUT;
1017 		nready = poll(fds, 1, INFTIM);
1018 		ASSERT_LE(0, nready) << strerror(errno);
1019 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
1020 		ASSERT_TRUE(fds[0].revents & POLLOUT);
1021 		break;
1022 	case SELECT:
1023 		FD_ZERO(&writefds);
1024 		FD_SET(m_fuse_fd, &writefds);
1025 		nfds = m_fuse_fd + 1;
1026 		nready = select(nfds, NULL, &writefds, NULL, NULL);
1027 		ASSERT_LE(0, nready) << strerror(errno);
1028 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
1029 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1030 		break;
1031 	default:
1032 		FAIL() << "not yet implemented";
1033 	}
1034 	r = write(m_fuse_fd, &out, out.header.len);
1035 	if (out.expected_errno) {
1036 		ASSERT_EQ(-1, r);
1037 		ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1038 	} else {
1039 		ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1040 	}
1041 }
1042 
1043 void* MockFS::service(void *pthr_data) {
1044 	MockFS *mock_fs = (MockFS*)pthr_data;
1045 
1046 	mock_fs->loop();
1047 
1048 	return (NULL);
1049 }
1050 
1051 void MockFS::unmount() {
1052 	::unmount("mountpoint", 0);
1053 }
1054