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