xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 6c05f3a74f30934ee60919cc97e16ec69b542b06)
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_read, int max_readahead, bool allow_other,
420 	bool default_permissions,
421 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
422 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
423 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
424 	const char *fsname, const char *subtype)
425 	: m_daemon_id(NULL),
426 	  m_kernel_minor_version(kernel_minor_version),
427 	  m_kq(pm == KQ ? kqueue() : -1),
428 	  m_maxread(max_read),
429 	  m_maxreadahead(max_readahead),
430 	  m_pid(getpid()),
431 	  m_uniques(new std::unordered_set<uint64_t>),
432 	  m_pm(pm),
433 	  m_time_gran(time_gran),
434 	  m_child_pid(-1),
435 	  m_maxwrite(MIN(max_write, max_max_write)),
436 	  m_nready(-1),
437 	  m_quit(false)
438 {
439 	struct sigaction sa;
440 	struct iovec *iov = NULL;
441 	int iovlen = 0;
442 	char fdstr[15];
443 	const bool trueval = true;
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 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
470 	build_iovec(&iov, &iovlen, "fspath",
471 		    __DECONST(void *, "mountpoint"), -1);
472 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
473 	sprintf(fdstr, "%d", m_fuse_fd);
474 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
475 	if (m_maxread > 0) {
476 		char val[10];
477 
478 		snprintf(val, sizeof(val), "%d", m_maxread);
479 		build_iovec(&iov, &iovlen, "max_read=", &val, -1);
480 	}
481 	if (allow_other) {
482 		build_iovec(&iov, &iovlen, "allow_other",
483 			__DECONST(void*, &trueval), sizeof(bool));
484 	}
485 	if (default_permissions) {
486 		build_iovec(&iov, &iovlen, "default_permissions",
487 			__DECONST(void*, &trueval), sizeof(bool));
488 	}
489 	if (push_symlinks_in) {
490 		build_iovec(&iov, &iovlen, "push_symlinks_in",
491 			__DECONST(void*, &trueval), sizeof(bool));
492 	}
493 	if (ro) {
494 		build_iovec(&iov, &iovlen, "ro",
495 			__DECONST(void*, &trueval), sizeof(bool));
496 	}
497 	if (async) {
498 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
499 			sizeof(bool));
500 	}
501 	if (noatime) {
502 		build_iovec(&iov, &iovlen, "noatime",
503 			__DECONST(void*, &trueval), sizeof(bool));
504 	}
505 	if (noclusterr) {
506 		build_iovec(&iov, &iovlen, "noclusterr",
507 			__DECONST(void*, &trueval), sizeof(bool));
508 	}
509 	if (nointr) {
510 		build_iovec(&iov, &iovlen, "nointr",
511 			__DECONST(void*, &trueval), sizeof(bool));
512 	} else {
513 		build_iovec(&iov, &iovlen, "intr",
514 			__DECONST(void*, &trueval), sizeof(bool));
515 	}
516 	if (*fsname) {
517 		build_iovec(&iov, &iovlen, "fsname=",
518 			__DECONST(void*, fsname), -1);
519 	}
520 	if (*subtype) {
521 		build_iovec(&iov, &iovlen, "subtype=",
522 			__DECONST(void*, subtype), -1);
523 	}
524 	if (nmount(iov, iovlen, 0))
525 		throw(std::system_error(errno, std::system_category(),
526 			"Couldn't mount filesystem"));
527 	free_iovec(&iov, &iovlen);
528 
529 	// Setup default handler
530 	ON_CALL(*this, process(_, _))
531 		.WillByDefault(Invoke(this, &MockFS::process_default));
532 
533 	init(flags);
534 	bzero(&sa, sizeof(sa));
535 	sa.sa_handler = sigint_handler;
536 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
537 	if (0 != sigaction(SIGUSR1, &sa, NULL))
538 		throw(std::system_error(errno, std::system_category(),
539 			"Couldn't handle SIGUSR1"));
540 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
541 		throw(std::system_error(errno, std::system_category(),
542 			"Couldn't Couldn't start fuse thread"));
543 }
544 
545 MockFS::~MockFS() {
546 	kill_daemon();
547 	if (m_daemon_id != NULL) {
548 		pthread_join(m_daemon_id, NULL);
549 		m_daemon_id = NULL;
550 	}
551 	::unmount("mountpoint", MNT_FORCE);
552 	rmdir("mountpoint");
553 	if (m_kq >= 0)
554 		close(m_kq);
555 }
556 
557 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
558 	uint32_t inlen = in.header.len;
559 	size_t fih = sizeof(in.header);
560 	switch (in.header.opcode) {
561 	case FUSE_LOOKUP:
562 	case FUSE_RMDIR:
563 	case FUSE_SYMLINK:
564 	case FUSE_UNLINK:
565 		EXPECT_GT(inlen, fih) << "Missing request filename";
566 		// No redundant information for checking buflen
567 		break;
568 	case FUSE_FORGET:
569 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
570 		EXPECT_EQ((size_t)buflen, inlen);
571 		break;
572 	case FUSE_GETATTR:
573 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
574 		EXPECT_EQ((size_t)buflen, inlen);
575 		break;
576 	case FUSE_SETATTR:
577 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
578 		EXPECT_EQ((size_t)buflen, inlen);
579 		break;
580 	case FUSE_READLINK:
581 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
582 		EXPECT_EQ((size_t)buflen, inlen);
583 		break;
584 	case FUSE_MKNOD:
585 		{
586 			size_t s;
587 			if (m_kernel_minor_version >= 12)
588 				s = sizeof(in.body.mknod);
589 			else
590 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
591 			EXPECT_GE(inlen, fih + s) << "Missing request body";
592 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
593 			// No redundant information for checking buflen
594 			break;
595 		}
596 	case FUSE_MKDIR:
597 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
598 			"Missing request body";
599 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
600 			"Missing request filename";
601 		// No redundant information for checking buflen
602 		break;
603 	case FUSE_RENAME:
604 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
605 			"Missing request body";
606 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
607 			"Missing request filename";
608 		// No redundant information for checking buflen
609 		break;
610 	case FUSE_LINK:
611 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
612 			"Missing request body";
613 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
614 			"Missing request filename";
615 		// No redundant information for checking buflen
616 		break;
617 	case FUSE_OPEN:
618 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
619 		EXPECT_EQ((size_t)buflen, inlen);
620 		break;
621 	case FUSE_READ:
622 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
623 		EXPECT_EQ((size_t)buflen, inlen);
624 		break;
625 	case FUSE_WRITE:
626 		{
627 			size_t s;
628 
629 			if (m_kernel_minor_version >= 9)
630 				s = sizeof(in.body.write);
631 			else
632 				s = FUSE_COMPAT_WRITE_IN_SIZE;
633 			// I suppose a 0-byte write should be allowed
634 			EXPECT_GE(inlen, fih + s) << "Missing request body";
635 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
636 			break;
637 		}
638 	case FUSE_DESTROY:
639 	case FUSE_STATFS:
640 		EXPECT_EQ(inlen, fih);
641 		EXPECT_EQ((size_t)buflen, inlen);
642 		break;
643 	case FUSE_RELEASE:
644 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
645 		EXPECT_EQ((size_t)buflen, inlen);
646 		break;
647 	case FUSE_FSYNC:
648 	case FUSE_FSYNCDIR:
649 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
650 		EXPECT_EQ((size_t)buflen, inlen);
651 		break;
652 	case FUSE_SETXATTR:
653 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
654 			"Missing request body";
655 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
656 			"Missing request attribute name";
657 		// No redundant information for checking buflen
658 		break;
659 	case FUSE_GETXATTR:
660 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
661 			"Missing request body";
662 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
663 			"Missing request attribute name";
664 		// No redundant information for checking buflen
665 		break;
666 	case FUSE_LISTXATTR:
667 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
668 		EXPECT_EQ((size_t)buflen, inlen);
669 		break;
670 	case FUSE_REMOVEXATTR:
671 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
672 		// No redundant information for checking buflen
673 		break;
674 	case FUSE_FLUSH:
675 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
676 		EXPECT_EQ((size_t)buflen, inlen);
677 		break;
678 	case FUSE_INIT:
679 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
680 		EXPECT_EQ((size_t)buflen, inlen);
681 		break;
682 	case FUSE_OPENDIR:
683 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
684 		EXPECT_EQ((size_t)buflen, inlen);
685 		break;
686 	case FUSE_READDIR:
687 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
688 		EXPECT_EQ((size_t)buflen, inlen);
689 		break;
690 	case FUSE_RELEASEDIR:
691 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
692 		EXPECT_EQ((size_t)buflen, inlen);
693 		break;
694 	case FUSE_GETLK:
695 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
696 		EXPECT_EQ((size_t)buflen, inlen);
697 		break;
698 	case FUSE_SETLK:
699 	case FUSE_SETLKW:
700 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
701 		EXPECT_EQ((size_t)buflen, inlen);
702 		break;
703 	case FUSE_ACCESS:
704 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
705 		EXPECT_EQ((size_t)buflen, inlen);
706 		break;
707 	case FUSE_CREATE:
708 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
709 			"Missing request body";
710 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
711 			"Missing request filename";
712 		// No redundant information for checking buflen
713 		break;
714 	case FUSE_INTERRUPT:
715 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
716 		EXPECT_EQ((size_t)buflen, inlen);
717 		break;
718 	case FUSE_FALLOCATE:
719 		EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
720 		EXPECT_EQ((size_t)buflen, inlen);
721 		break;
722 	case FUSE_BMAP:
723 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
724 		EXPECT_EQ((size_t)buflen, inlen);
725 		break;
726 	case FUSE_LSEEK:
727 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
728 		EXPECT_EQ((size_t)buflen, inlen);
729 		break;
730 	case FUSE_COPY_FILE_RANGE:
731 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
732 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
733 		EXPECT_EQ((size_t)buflen, inlen);
734 		break;
735 	case FUSE_NOTIFY_REPLY:
736 	case FUSE_BATCH_FORGET:
737 	case FUSE_IOCTL:
738 	case FUSE_POLL:
739 	case FUSE_READDIRPLUS:
740 		FAIL() << "Unsupported opcode?";
741 	default:
742 		FAIL() << "Unknown opcode " << in.header.opcode;
743 	}
744 	/* Verify that the ticket's unique value is actually unique. */
745 	if (m_uniques->find(in.header.unique) != m_uniques->end())
746 		FAIL() << "Non-unique \"unique\" value";
747 	m_uniques->insert(in.header.unique);
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 		if (r <= 0 && errno == EINVAL) {
1038 			printf("Failed to write response.  unique=%" PRIu64
1039 			    ":\n", out.header.unique);
1040 		}
1041 		ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1042 	}
1043 }
1044 
1045 void* MockFS::service(void *pthr_data) {
1046 	MockFS *mock_fs = (MockFS*)pthr_data;
1047 
1048 	mock_fs->loop();
1049 
1050 	return (NULL);
1051 }
1052 
1053 void MockFS::unmount() {
1054 	::unmount("mountpoint", 0);
1055 }
1056