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