xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision b7d2f7c1a68c270ae5f46b8cfcfb85aa792b6b12)
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 (!no_auto_init)
554 		start_service();
555 }
556 
~MockFS()557 MockFS::~MockFS() {
558 	kill_daemon();
559 	join_daemon();
560 	::unmount("mountpoint", MNT_FORCE);
561 	rmdir("mountpoint");
562 	if (m_kq >= 0)
563 		close(m_kq);
564 }
565 
audit_request(const mockfs_buf_in & in,ssize_t buflen)566 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
567 	uint32_t inlen = in.header.len;
568 	size_t fih = sizeof(in.header);
569 	switch (in.header.opcode) {
570 	case FUSE_LOOKUP:
571 	case FUSE_RMDIR:
572 	case FUSE_SYMLINK:
573 	case FUSE_UNLINK:
574 		EXPECT_GT(inlen, fih) << "Missing request filename";
575 		// No redundant information for checking buflen
576 		break;
577 	case FUSE_FORGET:
578 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
579 		EXPECT_EQ((size_t)buflen, inlen);
580 		break;
581 	case FUSE_GETATTR:
582 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
583 		EXPECT_EQ((size_t)buflen, inlen);
584 		break;
585 	case FUSE_SETATTR:
586 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
587 		EXPECT_EQ((size_t)buflen, inlen);
588 		break;
589 	case FUSE_READLINK:
590 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
591 		EXPECT_EQ((size_t)buflen, inlen);
592 		break;
593 	case FUSE_MKNOD:
594 		{
595 			size_t s;
596 			if (m_kernel_minor_version >= 12)
597 				s = sizeof(in.body.mknod);
598 			else
599 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
600 			EXPECT_GE(inlen, fih + s) << "Missing request body";
601 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
602 			// No redundant information for checking buflen
603 			break;
604 		}
605 	case FUSE_MKDIR:
606 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
607 			"Missing request body";
608 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
609 			"Missing request filename";
610 		// No redundant information for checking buflen
611 		break;
612 	case FUSE_RENAME:
613 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
614 			"Missing request body";
615 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
616 			"Missing request filename";
617 		// No redundant information for checking buflen
618 		break;
619 	case FUSE_LINK:
620 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
621 			"Missing request body";
622 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
623 			"Missing request filename";
624 		// No redundant information for checking buflen
625 		break;
626 	case FUSE_OPEN:
627 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
628 		EXPECT_EQ((size_t)buflen, inlen);
629 		break;
630 	case FUSE_READ:
631 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
632 		EXPECT_EQ((size_t)buflen, inlen);
633 		break;
634 	case FUSE_WRITE:
635 		{
636 			size_t s;
637 
638 			if (m_kernel_minor_version >= 9)
639 				s = sizeof(in.body.write);
640 			else
641 				s = FUSE_COMPAT_WRITE_IN_SIZE;
642 			// I suppose a 0-byte write should be allowed
643 			EXPECT_GE(inlen, fih + s) << "Missing request body";
644 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
645 			break;
646 		}
647 	case FUSE_DESTROY:
648 	case FUSE_STATFS:
649 		EXPECT_EQ(inlen, fih);
650 		EXPECT_EQ((size_t)buflen, inlen);
651 		break;
652 	case FUSE_RELEASE:
653 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
654 		EXPECT_EQ((size_t)buflen, inlen);
655 		break;
656 	case FUSE_FSYNC:
657 	case FUSE_FSYNCDIR:
658 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
659 		EXPECT_EQ((size_t)buflen, inlen);
660 		break;
661 	case FUSE_SETXATTR:
662 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
663 			"Missing request body";
664 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
665 			"Missing request attribute name";
666 		// No redundant information for checking buflen
667 		break;
668 	case FUSE_GETXATTR:
669 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
670 			"Missing request body";
671 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
672 			"Missing request attribute name";
673 		// No redundant information for checking buflen
674 		break;
675 	case FUSE_LISTXATTR:
676 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
677 		EXPECT_EQ((size_t)buflen, inlen);
678 		break;
679 	case FUSE_REMOVEXATTR:
680 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
681 		// No redundant information for checking buflen
682 		break;
683 	case FUSE_FLUSH:
684 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
685 		EXPECT_EQ((size_t)buflen, inlen);
686 		break;
687 	case FUSE_INIT:
688 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
689 		EXPECT_EQ((size_t)buflen, inlen);
690 		break;
691 	case FUSE_IOCTL:
692 		EXPECT_GE(inlen, fih + sizeof(in.body.ioctl));
693 		EXPECT_EQ(inlen,
694 			fih + sizeof(in.body.ioctl) + in.body.ioctl.in_size);
695 		EXPECT_EQ((size_t)buflen, inlen);
696 		break;
697 	case FUSE_OPENDIR:
698 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
699 		EXPECT_EQ((size_t)buflen, inlen);
700 		break;
701 	case FUSE_READDIR:
702 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
703 		EXPECT_EQ((size_t)buflen, inlen);
704 		break;
705 	case FUSE_RELEASEDIR:
706 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
707 		EXPECT_EQ((size_t)buflen, inlen);
708 		break;
709 	case FUSE_GETLK:
710 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
711 		EXPECT_EQ((size_t)buflen, inlen);
712 		break;
713 	case FUSE_SETLK:
714 	case FUSE_SETLKW:
715 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
716 		EXPECT_EQ((size_t)buflen, inlen);
717 		break;
718 	case FUSE_ACCESS:
719 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
720 		EXPECT_EQ((size_t)buflen, inlen);
721 		break;
722 	case FUSE_CREATE:
723 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
724 			"Missing request body";
725 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
726 			"Missing request filename";
727 		// No redundant information for checking buflen
728 		break;
729 	case FUSE_INTERRUPT:
730 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
731 		EXPECT_EQ((size_t)buflen, inlen);
732 		break;
733 	case FUSE_FALLOCATE:
734 		EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
735 		EXPECT_EQ((size_t)buflen, inlen);
736 		break;
737 	case FUSE_BMAP:
738 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
739 		EXPECT_EQ((size_t)buflen, inlen);
740 		break;
741 	case FUSE_LSEEK:
742 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
743 		EXPECT_EQ((size_t)buflen, inlen);
744 		break;
745 	case FUSE_COPY_FILE_RANGE:
746 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
747 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
748 		EXPECT_EQ((size_t)buflen, inlen);
749 		break;
750 	case FUSE_NOTIFY_REPLY:
751 	case FUSE_BATCH_FORGET:
752 	case FUSE_POLL:
753 	case FUSE_READDIRPLUS:
754 		FAIL() << "Unsupported opcode?";
755 	default:
756 		FAIL() << "Unknown opcode " << in.header.opcode;
757 	}
758 	/* Verify that the ticket's unique value is actually unique. */
759 	if (m_uniques->find(in.header.unique) != m_uniques->end())
760 		FAIL() << "Non-unique \"unique\" value";
761 	m_uniques->insert(in.header.unique);
762 }
763 
init(uint32_t flags)764 void MockFS::init(uint32_t flags) {
765 	ssize_t buflen;
766 
767 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
768 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
769 
770 	read_request(*in, buflen);
771 	if (verbosity > 0)
772 		debug_request(*in, buflen);
773 	audit_request(*in, buflen);
774 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
775 
776 	out->header.unique = in->header.unique;
777 	out->header.error = 0;
778 	out->body.init.major = FUSE_KERNEL_VERSION;
779 	out->body.init.minor = m_kernel_minor_version;;
780 	out->body.init.flags = in->body.init.flags & flags;
781 	out->body.init.max_write = m_maxwrite;
782 	out->body.init.max_readahead = m_maxreadahead;
783 
784 	if (m_kernel_minor_version < 23) {
785 		SET_OUT_HEADER_LEN(*out, init_7_22);
786 	} else {
787 		out->body.init.time_gran = m_time_gran;
788 		SET_OUT_HEADER_LEN(*out, init);
789 	}
790 
791 	write(m_fuse_fd, out.get(), out->header.len);
792 }
793 
dup_dev_fuse()794 int MockFS::dup_dev_fuse()
795 {
796 	return (dup(m_fuse_fd));
797 }
798 
kill_daemon()799 void MockFS::kill_daemon() {
800 	m_quit = true;
801 	if (m_daemon_id != NULL)
802 		pthread_kill(m_daemon_id, SIGUSR1);
803 	// Closing the /dev/fuse file descriptor first allows unmount to
804 	// succeed even if the daemon doesn't correctly respond to commands
805 	// during the unmount sequence.
806 	close(m_fuse_fd);
807 	m_fuse_fd = -1;
808 }
809 
join_daemon()810 void MockFS::join_daemon() {
811 	if (m_daemon_id != NULL) {
812 		pthread_join(m_daemon_id, NULL);
813 		m_daemon_id = NULL;
814 	}
815 }
816 
loop()817 void MockFS::loop() {
818 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
819 
820 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
821 	ASSERT_TRUE(in != NULL);
822 	while (!m_quit) {
823 		ssize_t buflen;
824 
825 		bzero(in.get(), sizeof(*in));
826 		read_request(*in, buflen);
827 		if (m_quit)
828 			break;
829 		if (verbosity > 0)
830 			debug_request(*in, buflen);
831 		audit_request(*in, buflen);
832 		if (pid_ok((pid_t)in->header.pid)) {
833 			process(*in, out);
834 		} else {
835 			/*
836 			 * Reject any requests from unknown processes.  Because
837 			 * we actually do mount a filesystem, plenty of
838 			 * unrelated system daemons may try to access it.
839 			 */
840 			if (verbosity > 1)
841 				printf("\tREJECTED (wrong pid %d)\n",
842 					in->header.pid);
843 			process_default(*in, out);
844 		}
845 		for (auto &it: out)
846 			write_response(*it);
847 		out.clear();
848 	}
849 }
850 
notify_inval_entry(ino_t parent,const char * name,size_t namelen,int expected_errno)851 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen,
852 		int expected_errno)
853 {
854 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
855 
856 	out->expected_errno = expected_errno;
857 	out->header.unique = 0;	/* 0 means asynchronous notification */
858 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
859 	out->body.inval_entry.parent = parent;
860 	out->body.inval_entry.namelen = namelen;
861 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
862 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
863 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
864 		namelen;
865 	debug_response(*out);
866 	write_response(*out);
867 	return 0;
868 }
869 
notify_inval_inode(ino_t ino,off_t off,ssize_t len)870 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
871 {
872 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
873 
874 	out->header.unique = 0;	/* 0 means asynchronous notification */
875 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
876 	out->body.inval_inode.ino = ino;
877 	out->body.inval_inode.off = off;
878 	out->body.inval_inode.len = len;
879 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
880 	debug_response(*out);
881 	write_response(*out);
882 	return 0;
883 }
884 
notify_store(ino_t ino,off_t off,const void * data,ssize_t size)885 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
886 {
887 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
888 
889 	out->header.unique = 0;	/* 0 means asynchronous notification */
890 	out->header.error = FUSE_NOTIFY_STORE;
891 	out->body.store.nodeid = ino;
892 	out->body.store.offset = off;
893 	out->body.store.size = size;
894 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
895 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
896 	debug_response(*out);
897 	write_response(*out);
898 	return 0;
899 }
900 
pid_ok(pid_t pid)901 bool MockFS::pid_ok(pid_t pid) {
902 	if (pid == m_pid) {
903 		return (true);
904 	} else if (pid == m_child_pid) {
905 		return (true);
906 	} else {
907 		struct kinfo_proc *ki;
908 		bool ok = false;
909 
910 		ki = kinfo_getproc(pid);
911 		if (ki == NULL)
912 			return (false);
913 		/*
914 		 * Allow access by the aio daemon processes so that our tests
915 		 * can use aio functions
916 		 */
917 		if (0 == strncmp("aiod", ki->ki_comm, 4))
918 			ok = true;
919 		free(ki);
920 		return (ok);
921 	}
922 }
923 
process_default(const mockfs_buf_in & in,std::vector<std::unique_ptr<mockfs_buf_out>> & out)924 void MockFS::process_default(const mockfs_buf_in& in,
925 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
926 {
927 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
928 	out0->header.unique = in.header.unique;
929 	out0->header.error = -EOPNOTSUPP;
930 	out0->header.len = sizeof(out0->header);
931 	out.push_back(std::move(out0));
932 }
933 
read_request(mockfs_buf_in & in,ssize_t & res)934 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
935 	int nready = 0;
936 	fd_set readfds;
937 	pollfd fds[1];
938 	struct kevent changes[1];
939 	struct kevent events[1];
940 	struct timespec timeout_ts;
941 	struct timeval timeout_tv;
942 	const int timeout_ms = 999;
943 	int timeout_int, nfds;
944 	int fuse_fd;
945 
946 	switch (m_pm) {
947 	case BLOCKING:
948 		break;
949 	case KQ:
950 		timeout_ts.tv_sec = 0;
951 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
952 		while (nready == 0) {
953 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
954 				EV_ADD | EV_ONESHOT, 0, 0, 0);
955 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
956 				&timeout_ts);
957 			if (m_quit)
958 				return;
959 		}
960 		ASSERT_LE(0, nready) << strerror(errno);
961 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
962 		if (events[0].flags & EV_ERROR)
963 			FAIL() << strerror(events[0].data);
964 		else if (events[0].flags & EV_EOF)
965 			FAIL() << strerror(events[0].fflags);
966 		m_nready = events[0].data;
967 		break;
968 	case POLL:
969 		timeout_int = timeout_ms;
970 		fds[0].fd = m_fuse_fd;
971 		fds[0].events = POLLIN;
972 		while (nready == 0) {
973 			nready = poll(fds, 1, timeout_int);
974 			if (m_quit)
975 				return;
976 		}
977 		ASSERT_LE(0, nready) << strerror(errno);
978 		ASSERT_TRUE(fds[0].revents & POLLIN);
979 		break;
980 	case SELECT:
981 		fuse_fd = m_fuse_fd;
982 		if (fuse_fd < 0)
983 			break;
984 		timeout_tv.tv_sec = 0;
985 		timeout_tv.tv_usec = timeout_ms * 1'000;
986 		nfds = fuse_fd + 1;
987 		while (nready == 0) {
988 			FD_ZERO(&readfds);
989 			FD_SET(fuse_fd, &readfds);
990 			nready = select(nfds, &readfds, NULL, NULL,
991 				&timeout_tv);
992 			if (m_quit)
993 				return;
994 		}
995 		ASSERT_LE(0, nready) << strerror(errno);
996 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
997 		break;
998 	default:
999 		FAIL() << "not yet implemented";
1000 	}
1001 	res = read(m_fuse_fd, &in, sizeof(in));
1002 
1003 	if (res < 0 && errno == ENODEV && m_expect_unmount) {
1004 		/* The kernel unmounted us, as expected. */
1005 		m_quit = true;
1006 	}
1007 	if (res < 0 && errno != EBADF && !m_quit) {
1008 		m_quit = true;
1009 		FAIL() << "read: " << strerror(errno);
1010 	}
1011 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
1012 	/*
1013 	 * Inconsistently, fuse_in_header.len is the size of the entire
1014 	 * request,including header, even though fuse_out_header.len excludes
1015 	 * the size of the header.
1016 	 */
1017 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
1018 }
1019 
start_service()1020 void MockFS::start_service() {
1021 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
1022 		throw(std::system_error(errno, std::system_category(),
1023 			"Couldn't start fuse thread"));
1024 }
1025 
write_response(const mockfs_buf_out & out)1026 void MockFS::write_response(const mockfs_buf_out &out) {
1027 	fd_set writefds;
1028 	pollfd fds[1];
1029 	struct kevent changes[1];
1030 	struct kevent events[1];
1031 	int nready, nfds;
1032 	ssize_t r;
1033 
1034 	switch (m_pm) {
1035 	case BLOCKING:
1036 		break;
1037 	case KQ:
1038 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1039 			EV_ADD | EV_ONESHOT, 0, 0, 0);
1040 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1041 			NULL);
1042 		ASSERT_LE(0, nready) << strerror(errno);
1043 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1044 		if (events[0].flags & EV_ERROR)
1045 			FAIL() << strerror(events[0].data);
1046 		else if (events[0].flags & EV_EOF)
1047 			FAIL() << strerror(events[0].fflags);
1048 		m_nready = events[0].data;
1049 		break;
1050 	case POLL:
1051 		fds[0].fd = m_fuse_fd;
1052 		fds[0].events = POLLOUT;
1053 		nready = poll(fds, 1, INFTIM);
1054 		ASSERT_LE(0, nready) << strerror(errno);
1055 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
1056 		ASSERT_TRUE(fds[0].revents & POLLOUT);
1057 		break;
1058 	case SELECT:
1059 		FD_ZERO(&writefds);
1060 		FD_SET(m_fuse_fd, &writefds);
1061 		nfds = m_fuse_fd + 1;
1062 		nready = select(nfds, NULL, &writefds, NULL, NULL);
1063 		ASSERT_LE(0, nready) << strerror(errno);
1064 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
1065 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1066 		break;
1067 	default:
1068 		FAIL() << "not yet implemented";
1069 	}
1070 	r = write(m_fuse_fd, &out, out.header.len);
1071 	if (out.expected_errno) {
1072 		ASSERT_EQ(-1, r);
1073 		ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1074 	} else if (m_quit && errno == EBADF) {
1075 		/* Daemon is in the process of shutting down */
1076 	} else {
1077 		if (r <= 0 && errno == EINVAL) {
1078 			printf("Failed to write response.  unique=%" PRIu64
1079 			    ":\n", out.header.unique);
1080 		}
1081 		ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1082 	}
1083 }
1084 
service(void * pthr_data)1085 void* MockFS::service(void *pthr_data) {
1086 	MockFS *mock_fs = (MockFS*)pthr_data;
1087 
1088 	mock_fs->loop();
1089 
1090 	return (NULL);
1091 }
1092 
unmount()1093 void MockFS::unmount() {
1094 	::unmount("mountpoint", 0);
1095 }
1096