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