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