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