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