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