xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 9768746b)
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_GETLK:
233 			printf(" fh=%#" PRIx64
234 				" type=%u pid=%u",
235 				in.body.getlk.fh,
236 				in.body.getlk.lk.type,
237 				in.body.getlk.lk.pid);
238 			if (verbosity >= 2) {
239 				printf(" range=[%" PRIi64 ":%" PRIi64 "]",
240 					in.body.getlk.lk.start,
241 					in.body.getlk.lk.end);
242 			}
243 			break;
244 		case FUSE_INTERRUPT:
245 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
246 			break;
247 		case FUSE_LINK:
248 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
249 			break;
250 		case FUSE_LISTXATTR:
251 			printf(" size=%" PRIu32, in.body.listxattr.size);
252 			break;
253 		case FUSE_LOOKUP:
254 			printf(" %s", in.body.lookup);
255 			break;
256 		case FUSE_LSEEK:
257 			switch (in.body.lseek.whence) {
258 			case SEEK_HOLE:
259 				printf(" SEEK_HOLE offset=%jd",
260 				    in.body.lseek.offset);
261 				break;
262 			case SEEK_DATA:
263 				printf(" SEEK_DATA offset=%jd",
264 				    in.body.lseek.offset);
265 				break;
266 			default:
267 				printf(" whence=%u offset=%jd",
268 				    in.body.lseek.whence, in.body.lseek.offset);
269 				break;
270 			}
271 			break;
272 		case FUSE_MKDIR:
273 			name = (const char*)in.body.bytes +
274 				sizeof(fuse_mkdir_in);
275 			printf(" name=%s mode=%#o umask=%#o", name,
276 				in.body.mkdir.mode, in.body.mkdir.umask);
277 			break;
278 		case FUSE_MKNOD:
279 			if (m_kernel_minor_version >= 12)
280 				name = (const char*)in.body.bytes +
281 					sizeof(fuse_mknod_in);
282 			else
283 				name = (const char*)in.body.bytes +
284 					FUSE_COMPAT_MKNOD_IN_SIZE;
285 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
286 				in.body.mknod.mode, in.body.mknod.rdev,
287 				in.body.mknod.umask, name);
288 			break;
289 		case FUSE_OPEN:
290 			printf(" flags=%#x", in.body.open.flags);
291 			break;
292 		case FUSE_OPENDIR:
293 			printf(" flags=%#x", in.body.opendir.flags);
294 			break;
295 		case FUSE_READ:
296 			printf(" offset=%" PRIu64 " size=%u",
297 				in.body.read.offset,
298 				in.body.read.size);
299 			if (verbosity > 1)
300 				printf(" flags=%#x", in.body.read.flags);
301 			break;
302 		case FUSE_READDIR:
303 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
304 				in.body.readdir.fh, in.body.readdir.offset,
305 				in.body.readdir.size);
306 			break;
307 		case FUSE_RELEASE:
308 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
309 				in.body.release.fh,
310 				in.body.release.flags,
311 				in.body.release.lock_owner);
312 			break;
313 		case FUSE_RENAME:
314 			{
315 				const char *src = (const char*)in.body.bytes +
316 					sizeof(fuse_rename_in);
317 				const char *dst = src + strlen(src) + 1;
318 				printf(" src=%s newdir=%" PRIu64 " dst=%s",
319 					src, in.body.rename.newdir, dst);
320 			}
321 			break;
322 		case FUSE_SETATTR:
323 			if (verbosity <= 1) {
324 				printf(" valid=%#x", in.body.setattr.valid);
325 				break;
326 			}
327 			if (in.body.setattr.valid & FATTR_MODE)
328 				printf(" mode=%#o", in.body.setattr.mode);
329 			if (in.body.setattr.valid & FATTR_UID)
330 				printf(" uid=%u", in.body.setattr.uid);
331 			if (in.body.setattr.valid & FATTR_GID)
332 				printf(" gid=%u", in.body.setattr.gid);
333 			if (in.body.setattr.valid & FATTR_SIZE)
334 				printf(" size=%" PRIu64, in.body.setattr.size);
335 			if (in.body.setattr.valid & FATTR_ATIME)
336 				printf(" atime=%" PRIu64 ".%u",
337 					in.body.setattr.atime,
338 					in.body.setattr.atimensec);
339 			if (in.body.setattr.valid & FATTR_MTIME)
340 				printf(" mtime=%" PRIu64 ".%u",
341 					in.body.setattr.mtime,
342 					in.body.setattr.mtimensec);
343 			if (in.body.setattr.valid & FATTR_FH)
344 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
345 			break;
346 		case FUSE_SETLK:
347 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
348 				" type=%u pid=%u",
349 				in.body.setlk.fh, in.body.setlk.owner,
350 				in.body.setlk.lk.type,
351 				in.body.setlk.lk.pid);
352 			if (verbosity >= 2) {
353 				printf(" range=[%" PRIi64 ":%" PRIi64 "]",
354 					in.body.setlk.lk.start,
355 					in.body.setlk.lk.end);
356 			}
357 			break;
358 		case FUSE_SETXATTR:
359 			/*
360 			 * In theory neither the xattr name and value need be
361 			 * ASCII, but in this test suite they always are.
362 			 */
363 			name = (const char*)in.body.bytes +
364 				sizeof(fuse_setxattr_in);
365 			value = name + strlen(name) + 1;
366 			printf(" %s=%s", name, value);
367 			break;
368 		case FUSE_WRITE:
369 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
370 				" size=%u write_flags=%u",
371 				in.body.write.fh,
372 				in.body.write.offset, in.body.write.size,
373 				in.body.write.write_flags);
374 			if (verbosity > 1)
375 				printf(" flags=%#x", in.body.write.flags);
376 			break;
377 		default:
378 			break;
379 	}
380 	printf("\n");
381 }
382 
383 /*
384  * Debug a FUSE response.
385  *
386  * This is mostly useful for asynchronous notifications, which don't correspond
387  * to any request
388  */
389 void MockFS::debug_response(const mockfs_buf_out &out) {
390 	const char *name;
391 
392 	if (verbosity == 0)
393 		return;
394 
395 	switch (out.header.error) {
396 		case FUSE_NOTIFY_INVAL_ENTRY:
397 			name = (const char*)out.body.bytes +
398 				sizeof(fuse_notify_inval_entry_out);
399 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
400 				out.body.inval_entry.parent, name);
401 			break;
402 		case FUSE_NOTIFY_INVAL_INODE:
403 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
404 				" len=%" PRIi64 "\n",
405 				out.body.inval_inode.ino,
406 				out.body.inval_inode.off,
407 				out.body.inval_inode.len);
408 			break;
409 		case FUSE_NOTIFY_STORE:
410 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
411 				" size=%" PRIu32 "\n",
412 				out.body.store.nodeid,
413 				out.body.store.offset,
414 				out.body.store.size);
415 			break;
416 		default:
417 			break;
418 	}
419 }
420 
421 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
422 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
423 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
424 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
425 	const char *fsname, const char *subtype)
426 {
427 	struct sigaction sa;
428 	struct iovec *iov = NULL;
429 	int iovlen = 0;
430 	char fdstr[15];
431 	const bool trueval = true;
432 
433 	m_daemon_id = NULL;
434 	m_expected_write_errno = 0;
435 	m_kernel_minor_version = kernel_minor_version;
436 	m_maxreadahead = max_readahead;
437 	m_maxwrite = MIN(max_write, max_max_write);
438 	m_nready = -1;
439 	m_pm = pm;
440 	m_time_gran = time_gran;
441 	m_quit = false;
442 	m_last_unique = 0;
443 	if (m_pm == KQ)
444 		m_kq = kqueue();
445 	else
446 		m_kq = -1;
447 
448 	/*
449 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
450 	 * mkdtemp
451 	 */
452 	/*
453 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
454 	 * instead.
455 	 */
456 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
457 		throw(std::system_error(errno, std::system_category(),
458 			"Couldn't make mountpoint directory"));
459 
460 	switch (m_pm) {
461 	case BLOCKING:
462 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
463 		break;
464 	default:
465 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
466 		break;
467 	}
468 	if (m_fuse_fd < 0)
469 		throw(std::system_error(errno, std::system_category(),
470 			"Couldn't open /dev/fuse"));
471 
472 	m_pid = getpid();
473 	m_child_pid = -1;
474 
475 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
476 	build_iovec(&iov, &iovlen, "fspath",
477 		    __DECONST(void *, "mountpoint"), -1);
478 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
479 	sprintf(fdstr, "%d", m_fuse_fd);
480 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
481 	if (allow_other) {
482 		build_iovec(&iov, &iovlen, "allow_other",
483 			__DECONST(void*, &trueval), sizeof(bool));
484 	}
485 	if (default_permissions) {
486 		build_iovec(&iov, &iovlen, "default_permissions",
487 			__DECONST(void*, &trueval), sizeof(bool));
488 	}
489 	if (push_symlinks_in) {
490 		build_iovec(&iov, &iovlen, "push_symlinks_in",
491 			__DECONST(void*, &trueval), sizeof(bool));
492 	}
493 	if (ro) {
494 		build_iovec(&iov, &iovlen, "ro",
495 			__DECONST(void*, &trueval), sizeof(bool));
496 	}
497 	if (async) {
498 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
499 			sizeof(bool));
500 	}
501 	if (noatime) {
502 		build_iovec(&iov, &iovlen, "noatime",
503 			__DECONST(void*, &trueval), sizeof(bool));
504 	}
505 	if (noclusterr) {
506 		build_iovec(&iov, &iovlen, "noclusterr",
507 			__DECONST(void*, &trueval), sizeof(bool));
508 	}
509 	if (nointr) {
510 		build_iovec(&iov, &iovlen, "nointr",
511 			__DECONST(void*, &trueval), sizeof(bool));
512 	} else {
513 		build_iovec(&iov, &iovlen, "intr",
514 			__DECONST(void*, &trueval), sizeof(bool));
515 	}
516 	if (*fsname) {
517 		build_iovec(&iov, &iovlen, "fsname=",
518 			__DECONST(void*, fsname), -1);
519 	}
520 	if (*subtype) {
521 		build_iovec(&iov, &iovlen, "subtype=",
522 			__DECONST(void*, subtype), -1);
523 	}
524 	if (nmount(iov, iovlen, 0))
525 		throw(std::system_error(errno, std::system_category(),
526 			"Couldn't mount filesystem"));
527 
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 	/*
744 	 * Check that the ticket's unique value is sequential.  Technically it
745 	 * doesn't need to be sequential, merely unique.  But the current
746 	 * fusefs driver _does_ make it sequential, and that's easy to check
747 	 * for.
748 	 */
749 	if (in.header.unique != ++m_last_unique)
750 		FAIL() << "Non-sequential unique value";
751 }
752 
753 void MockFS::init(uint32_t flags) {
754 	ssize_t buflen;
755 
756 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
757 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
758 
759 	read_request(*in, buflen);
760 	if (verbosity > 0)
761 		debug_request(*in, buflen);
762 	audit_request(*in, buflen);
763 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
764 
765 	out->header.unique = in->header.unique;
766 	out->header.error = 0;
767 	out->body.init.major = FUSE_KERNEL_VERSION;
768 	out->body.init.minor = m_kernel_minor_version;;
769 	out->body.init.flags = in->body.init.flags & flags;
770 	out->body.init.max_write = m_maxwrite;
771 	out->body.init.max_readahead = m_maxreadahead;
772 
773 	if (m_kernel_minor_version < 23) {
774 		SET_OUT_HEADER_LEN(*out, init_7_22);
775 	} else {
776 		out->body.init.time_gran = m_time_gran;
777 		SET_OUT_HEADER_LEN(*out, init);
778 	}
779 
780 	write(m_fuse_fd, out.get(), out->header.len);
781 }
782 
783 void MockFS::kill_daemon() {
784 	m_quit = true;
785 	if (m_daemon_id != NULL)
786 		pthread_kill(m_daemon_id, SIGUSR1);
787 	// Closing the /dev/fuse file descriptor first allows unmount to
788 	// succeed even if the daemon doesn't correctly respond to commands
789 	// during the unmount sequence.
790 	close(m_fuse_fd);
791 	m_fuse_fd = -1;
792 }
793 
794 void MockFS::loop() {
795 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
796 
797 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
798 	ASSERT_TRUE(in != NULL);
799 	while (!m_quit) {
800 		ssize_t buflen;
801 
802 		bzero(in.get(), sizeof(*in));
803 		read_request(*in, buflen);
804 		m_expected_write_errno = 0;
805 		if (m_quit)
806 			break;
807 		if (verbosity > 0)
808 			debug_request(*in, buflen);
809 		audit_request(*in, buflen);
810 		if (pid_ok((pid_t)in->header.pid)) {
811 			process(*in, out);
812 		} else {
813 			/*
814 			 * Reject any requests from unknown processes.  Because
815 			 * we actually do mount a filesystem, plenty of
816 			 * unrelated system daemons may try to access it.
817 			 */
818 			if (verbosity > 1)
819 				printf("\tREJECTED (wrong pid %d)\n",
820 					in->header.pid);
821 			process_default(*in, out);
822 		}
823 		for (auto &it: out)
824 			write_response(*it);
825 		out.clear();
826 	}
827 }
828 
829 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
830 {
831 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
832 
833 	out->header.unique = 0;	/* 0 means asynchronous notification */
834 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
835 	out->body.inval_entry.parent = parent;
836 	out->body.inval_entry.namelen = namelen;
837 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
838 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
839 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
840 		namelen;
841 	debug_response(*out);
842 	write_response(*out);
843 	return 0;
844 }
845 
846 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
847 {
848 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
849 
850 	out->header.unique = 0;	/* 0 means asynchronous notification */
851 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
852 	out->body.inval_inode.ino = ino;
853 	out->body.inval_inode.off = off;
854 	out->body.inval_inode.len = len;
855 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
856 	debug_response(*out);
857 	write_response(*out);
858 	return 0;
859 }
860 
861 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
862 {
863 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
864 
865 	out->header.unique = 0;	/* 0 means asynchronous notification */
866 	out->header.error = FUSE_NOTIFY_STORE;
867 	out->body.store.nodeid = ino;
868 	out->body.store.offset = off;
869 	out->body.store.size = size;
870 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
871 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
872 	debug_response(*out);
873 	write_response(*out);
874 	return 0;
875 }
876 
877 bool MockFS::pid_ok(pid_t pid) {
878 	if (pid == m_pid) {
879 		return (true);
880 	} else if (pid == m_child_pid) {
881 		return (true);
882 	} else {
883 		struct kinfo_proc *ki;
884 		bool ok = false;
885 
886 		ki = kinfo_getproc(pid);
887 		if (ki == NULL)
888 			return (false);
889 		/*
890 		 * Allow access by the aio daemon processes so that our tests
891 		 * can use aio functions
892 		 */
893 		if (0 == strncmp("aiod", ki->ki_comm, 4))
894 			ok = true;
895 		free(ki);
896 		return (ok);
897 	}
898 }
899 
900 void MockFS::process_default(const mockfs_buf_in& in,
901 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
902 {
903 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
904 	out0->header.unique = in.header.unique;
905 	out0->header.error = -EOPNOTSUPP;
906 	out0->header.len = sizeof(out0->header);
907 	out.push_back(std::move(out0));
908 }
909 
910 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
911 	int nready = 0;
912 	fd_set readfds;
913 	pollfd fds[1];
914 	struct kevent changes[1];
915 	struct kevent events[1];
916 	struct timespec timeout_ts;
917 	struct timeval timeout_tv;
918 	const int timeout_ms = 999;
919 	int timeout_int, nfds;
920 	int fuse_fd;
921 
922 	switch (m_pm) {
923 	case BLOCKING:
924 		break;
925 	case KQ:
926 		timeout_ts.tv_sec = 0;
927 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
928 		while (nready == 0) {
929 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
930 				EV_ADD | EV_ONESHOT, 0, 0, 0);
931 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
932 				&timeout_ts);
933 			if (m_quit)
934 				return;
935 		}
936 		ASSERT_LE(0, nready) << strerror(errno);
937 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
938 		if (events[0].flags & EV_ERROR)
939 			FAIL() << strerror(events[0].data);
940 		else if (events[0].flags & EV_EOF)
941 			FAIL() << strerror(events[0].fflags);
942 		m_nready = events[0].data;
943 		break;
944 	case POLL:
945 		timeout_int = timeout_ms;
946 		fds[0].fd = m_fuse_fd;
947 		fds[0].events = POLLIN;
948 		while (nready == 0) {
949 			nready = poll(fds, 1, timeout_int);
950 			if (m_quit)
951 				return;
952 		}
953 		ASSERT_LE(0, nready) << strerror(errno);
954 		ASSERT_TRUE(fds[0].revents & POLLIN);
955 		break;
956 	case SELECT:
957 		fuse_fd = m_fuse_fd;
958 		if (fuse_fd < 0)
959 			break;
960 		timeout_tv.tv_sec = 0;
961 		timeout_tv.tv_usec = timeout_ms * 1'000;
962 		nfds = fuse_fd + 1;
963 		while (nready == 0) {
964 			FD_ZERO(&readfds);
965 			FD_SET(fuse_fd, &readfds);
966 			nready = select(nfds, &readfds, NULL, NULL,
967 				&timeout_tv);
968 			if (m_quit)
969 				return;
970 		}
971 		ASSERT_LE(0, nready) << strerror(errno);
972 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
973 		break;
974 	default:
975 		FAIL() << "not yet implemented";
976 	}
977 	res = read(m_fuse_fd, &in, sizeof(in));
978 
979 	if (res < 0 && !m_quit) {
980 		m_quit = true;
981 		FAIL() << "read: " << strerror(errno);
982 	}
983 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
984 	/*
985 	 * Inconsistently, fuse_in_header.len is the size of the entire
986 	 * request,including header, even though fuse_out_header.len excludes
987 	 * the size of the header.
988 	 */
989 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
990 }
991 
992 void MockFS::write_response(const mockfs_buf_out &out) {
993 	fd_set writefds;
994 	pollfd fds[1];
995 	struct kevent changes[1];
996 	struct kevent events[1];
997 	int nready, nfds;
998 	ssize_t r;
999 
1000 	switch (m_pm) {
1001 	case BLOCKING:
1002 		break;
1003 	case KQ:
1004 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1005 			EV_ADD | EV_ONESHOT, 0, 0, 0);
1006 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1007 			NULL);
1008 		ASSERT_LE(0, nready) << strerror(errno);
1009 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1010 		if (events[0].flags & EV_ERROR)
1011 			FAIL() << strerror(events[0].data);
1012 		else if (events[0].flags & EV_EOF)
1013 			FAIL() << strerror(events[0].fflags);
1014 		m_nready = events[0].data;
1015 		break;
1016 	case POLL:
1017 		fds[0].fd = m_fuse_fd;
1018 		fds[0].events = POLLOUT;
1019 		nready = poll(fds, 1, INFTIM);
1020 		ASSERT_LE(0, nready) << strerror(errno);
1021 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
1022 		ASSERT_TRUE(fds[0].revents & POLLOUT);
1023 		break;
1024 	case SELECT:
1025 		FD_ZERO(&writefds);
1026 		FD_SET(m_fuse_fd, &writefds);
1027 		nfds = m_fuse_fd + 1;
1028 		nready = select(nfds, NULL, &writefds, NULL, NULL);
1029 		ASSERT_LE(0, nready) << strerror(errno);
1030 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
1031 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1032 		break;
1033 	default:
1034 		FAIL() << "not yet implemented";
1035 	}
1036 	r = write(m_fuse_fd, &out, out.header.len);
1037 	if (m_expected_write_errno) {
1038 		ASSERT_EQ(-1, r);
1039 		ASSERT_EQ(m_expected_write_errno, errno) << strerror(errno);
1040 	} else {
1041 		ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1042 	}
1043 }
1044 
1045 void* MockFS::service(void *pthr_data) {
1046 	MockFS *mock_fs = (MockFS*)pthr_data;
1047 
1048 	mock_fs->loop();
1049 
1050 	return (NULL);
1051 }
1052 
1053 void MockFS::unmount() {
1054 	::unmount("mountpoint", 0);
1055 }
1056