xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision d86025c1d49c84c4dc8c3635c83c078ad56e5a53)
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_LINK:
245 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
246 			break;
247 		case FUSE_LISTXATTR:
248 			printf(" size=%" PRIu32, in.body.listxattr.size);
249 			break;
250 		case FUSE_LOOKUP:
251 			printf(" %s", in.body.lookup);
252 			break;
253 		case FUSE_LSEEK:
254 			switch (in.body.lseek.whence) {
255 			case SEEK_HOLE:
256 				printf(" SEEK_HOLE offset=%jd",
257 				    in.body.lseek.offset);
258 				break;
259 			case SEEK_DATA:
260 				printf(" SEEK_DATA offset=%jd",
261 				    in.body.lseek.offset);
262 				break;
263 			default:
264 				printf(" whence=%u offset=%jd",
265 				    in.body.lseek.whence, in.body.lseek.offset);
266 				break;
267 			}
268 			break;
269 		case FUSE_MKDIR:
270 			name = (const char*)in.body.bytes +
271 				sizeof(fuse_mkdir_in);
272 			printf(" name=%s mode=%#o umask=%#o", name,
273 				in.body.mkdir.mode, in.body.mkdir.umask);
274 			break;
275 		case FUSE_MKNOD:
276 			if (m_kernel_minor_version >= 12)
277 				name = (const char*)in.body.bytes +
278 					sizeof(fuse_mknod_in);
279 			else
280 				name = (const char*)in.body.bytes +
281 					FUSE_COMPAT_MKNOD_IN_SIZE;
282 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
283 				in.body.mknod.mode, in.body.mknod.rdev,
284 				in.body.mknod.umask, name);
285 			break;
286 		case FUSE_OPEN:
287 			printf(" flags=%#x", in.body.open.flags);
288 			break;
289 		case FUSE_OPENDIR:
290 			printf(" flags=%#x", in.body.opendir.flags);
291 			break;
292 		case FUSE_READ:
293 			printf(" offset=%" PRIu64 " size=%u",
294 				in.body.read.offset,
295 				in.body.read.size);
296 			if (verbosity > 1)
297 				printf(" flags=%#x", in.body.read.flags);
298 			break;
299 		case FUSE_READDIR:
300 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
301 				in.body.readdir.fh, in.body.readdir.offset,
302 				in.body.readdir.size);
303 			break;
304 		case FUSE_RELEASE:
305 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
306 				in.body.release.fh,
307 				in.body.release.flags,
308 				in.body.release.lock_owner);
309 			break;
310 		case FUSE_RENAME:
311 			{
312 				const char *src = (const char*)in.body.bytes +
313 					sizeof(fuse_rename_in);
314 				const char *dst = src + strlen(src) + 1;
315 				printf(" src=%s newdir=%" PRIu64 " dst=%s",
316 					src, in.body.rename.newdir, dst);
317 			}
318 			break;
319 		case FUSE_SETATTR:
320 			if (verbosity <= 1) {
321 				printf(" valid=%#x", in.body.setattr.valid);
322 				break;
323 			}
324 			if (in.body.setattr.valid & FATTR_MODE)
325 				printf(" mode=%#o", in.body.setattr.mode);
326 			if (in.body.setattr.valid & FATTR_UID)
327 				printf(" uid=%u", in.body.setattr.uid);
328 			if (in.body.setattr.valid & FATTR_GID)
329 				printf(" gid=%u", in.body.setattr.gid);
330 			if (in.body.setattr.valid & FATTR_SIZE)
331 				printf(" size=%" PRIu64, in.body.setattr.size);
332 			if (in.body.setattr.valid & FATTR_ATIME)
333 				printf(" atime=%" PRIu64 ".%u",
334 					in.body.setattr.atime,
335 					in.body.setattr.atimensec);
336 			if (in.body.setattr.valid & FATTR_MTIME)
337 				printf(" mtime=%" PRIu64 ".%u",
338 					in.body.setattr.mtime,
339 					in.body.setattr.mtimensec);
340 			if (in.body.setattr.valid & FATTR_FH)
341 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
342 			break;
343 		case FUSE_SETLK:
344 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
345 				" type=%u pid=%u",
346 				in.body.setlk.fh, in.body.setlk.owner,
347 				in.body.setlk.lk.type,
348 				in.body.setlk.lk.pid);
349 			if (verbosity >= 2) {
350 				printf(" range=[%" PRIi64 ":%" PRIi64 "]",
351 					in.body.setlk.lk.start,
352 					in.body.setlk.lk.end);
353 			}
354 			break;
355 		case FUSE_SETXATTR:
356 			/*
357 			 * In theory neither the xattr name and value need be
358 			 * ASCII, but in this test suite they always are.
359 			 */
360 			name = (const char*)in.body.bytes +
361 				sizeof(fuse_setxattr_in);
362 			value = name + strlen(name) + 1;
363 			printf(" %s=%s", name, value);
364 			break;
365 		case FUSE_WRITE:
366 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
367 				" size=%u write_flags=%u",
368 				in.body.write.fh,
369 				in.body.write.offset, in.body.write.size,
370 				in.body.write.write_flags);
371 			if (verbosity > 1)
372 				printf(" flags=%#x", in.body.write.flags);
373 			break;
374 		default:
375 			break;
376 	}
377 	printf("\n");
378 }
379 
380 /*
381  * Debug a FUSE response.
382  *
383  * This is mostly useful for asynchronous notifications, which don't correspond
384  * to any request
385  */
debug_response(const mockfs_buf_out & out)386 void MockFS::debug_response(const mockfs_buf_out &out) {
387 	const char *name;
388 
389 	if (verbosity == 0)
390 		return;
391 
392 	switch (out.header.error) {
393 		case FUSE_NOTIFY_INVAL_ENTRY:
394 			name = (const char*)out.body.bytes +
395 				sizeof(fuse_notify_inval_entry_out);
396 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
397 				out.body.inval_entry.parent, name);
398 			break;
399 		case FUSE_NOTIFY_INVAL_INODE:
400 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
401 				" len=%" PRIi64 "\n",
402 				out.body.inval_inode.ino,
403 				out.body.inval_inode.off,
404 				out.body.inval_inode.len);
405 			break;
406 		case FUSE_NOTIFY_STORE:
407 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
408 				" size=%" PRIu32 "\n",
409 				out.body.store.nodeid,
410 				out.body.store.offset,
411 				out.body.store.size);
412 			break;
413 		default:
414 			break;
415 	}
416 }
417 
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)418 MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
419 	bool default_permissions,
420 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
421 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
422 	bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
423 	const char *fsname, const char *subtype, bool no_auto_init)
424 	: m_daemon_id(NULL),
425 	  m_kernel_minor_version(kernel_minor_version),
426 	  m_kq(pm == KQ ? kqueue() : -1),
427 	  m_maxread(max_read),
428 	  m_maxreadahead(max_readahead),
429 	  m_pid(getpid()),
430 	  m_uniques(new std::unordered_set<uint64_t>),
431 	  m_pm(pm),
432 	  m_time_gran(time_gran),
433 	  m_child_pid(-1),
434 	  m_maxwrite(MIN(max_write, max_max_write)),
435 	  m_nready(-1),
436 	  m_quit(false),
437 	  m_expect_unmount(false)
438 {
439 	struct sigaction sa;
440 	struct iovec *iov = NULL;
441 	int iovlen = 0;
442 	char fdstr[15];
443 	const bool trueval = true;
444 
445 	/*
446 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
447 	 * mkdtemp
448 	 */
449 	/*
450 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
451 	 * instead.
452 	 */
453 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
454 		throw(std::system_error(errno, std::system_category(),
455 			"Couldn't make mountpoint directory"));
456 
457 	switch (m_pm) {
458 	case BLOCKING:
459 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
460 		break;
461 	default:
462 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
463 		break;
464 	}
465 	if (m_fuse_fd < 0)
466 		throw(std::system_error(errno, std::system_category(),
467 			"Couldn't open /dev/fuse"));
468 
469 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
470 	build_iovec(&iov, &iovlen, "fspath",
471 		    __DECONST(void *, "mountpoint"), -1);
472 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
473 	sprintf(fdstr, "%d", m_fuse_fd);
474 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
475 	if (m_maxread > 0) {
476 		char val[12];
477 
478 		snprintf(val, sizeof(val), "%d", m_maxread);
479 		build_iovec(&iov, &iovlen, "max_read=", &val, -1);
480 	}
481 	if (allow_other) {
482 		build_iovec(&iov, &iovlen, "allow_other",
483 			__DECONST(void*, &trueval), sizeof(bool));
484 	}
485 	if (default_permissions) {
486 		build_iovec(&iov, &iovlen, "default_permissions",
487 			__DECONST(void*, &trueval), sizeof(bool));
488 	}
489 	if (push_symlinks_in) {
490 		build_iovec(&iov, &iovlen, "push_symlinks_in",
491 			__DECONST(void*, &trueval), sizeof(bool));
492 	}
493 	if (ro) {
494 		build_iovec(&iov, &iovlen, "ro",
495 			__DECONST(void*, &trueval), sizeof(bool));
496 	}
497 	if (async) {
498 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
499 			sizeof(bool));
500 	}
501 	if (noatime) {
502 		build_iovec(&iov, &iovlen, "noatime",
503 			__DECONST(void*, &trueval), sizeof(bool));
504 	}
505 	if (noclusterr) {
506 		build_iovec(&iov, &iovlen, "noclusterr",
507 			__DECONST(void*, &trueval), sizeof(bool));
508 	}
509 	if (nointr) {
510 		build_iovec(&iov, &iovlen, "nointr",
511 			__DECONST(void*, &trueval), sizeof(bool));
512 	} else {
513 		build_iovec(&iov, &iovlen, "intr",
514 			__DECONST(void*, &trueval), sizeof(bool));
515 	}
516 	if (*fsname) {
517 		build_iovec(&iov, &iovlen, "fsname=",
518 			__DECONST(void*, fsname), -1);
519 	}
520 	if (*subtype) {
521 		build_iovec(&iov, &iovlen, "subtype=",
522 			__DECONST(void*, subtype), -1);
523 	}
524 	if (nmount(iov, iovlen, 0))
525 		throw(std::system_error(errno, std::system_category(),
526 			"Couldn't mount filesystem"));
527 	free_iovec(&iov, &iovlen);
528 
529 	// Setup default handler
530 	ON_CALL(*this, process(_, _))
531 		.WillByDefault(Invoke(this, &MockFS::process_default));
532 
533 	if (!no_auto_init)
534 		init(flags);
535 
536 	bzero(&sa, sizeof(sa));
537 	sa.sa_handler = sigint_handler;
538 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
539 	if (0 != sigaction(SIGUSR1, &sa, NULL))
540 		throw(std::system_error(errno, std::system_category(),
541 			"Couldn't handle SIGUSR1"));
542 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
543 		throw(std::system_error(errno, std::system_category(),
544 			"Couldn't Couldn't start fuse thread"));
545 }
546 
~MockFS()547 MockFS::~MockFS() {
548 	kill_daemon();
549 	join_daemon();
550 	::unmount("mountpoint", MNT_FORCE);
551 	rmdir("mountpoint");
552 	if (m_kq >= 0)
553 		close(m_kq);
554 }
555 
audit_request(const mockfs_buf_in & in,ssize_t buflen)556 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
557 	uint32_t inlen = in.header.len;
558 	size_t fih = sizeof(in.header);
559 	switch (in.header.opcode) {
560 	case FUSE_LOOKUP:
561 	case FUSE_RMDIR:
562 	case FUSE_SYMLINK:
563 	case FUSE_UNLINK:
564 		EXPECT_GT(inlen, fih) << "Missing request filename";
565 		// No redundant information for checking buflen
566 		break;
567 	case FUSE_FORGET:
568 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
569 		EXPECT_EQ((size_t)buflen, inlen);
570 		break;
571 	case FUSE_GETATTR:
572 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
573 		EXPECT_EQ((size_t)buflen, inlen);
574 		break;
575 	case FUSE_SETATTR:
576 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
577 		EXPECT_EQ((size_t)buflen, inlen);
578 		break;
579 	case FUSE_READLINK:
580 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
581 		EXPECT_EQ((size_t)buflen, inlen);
582 		break;
583 	case FUSE_MKNOD:
584 		{
585 			size_t s;
586 			if (m_kernel_minor_version >= 12)
587 				s = sizeof(in.body.mknod);
588 			else
589 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
590 			EXPECT_GE(inlen, fih + s) << "Missing request body";
591 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
592 			// No redundant information for checking buflen
593 			break;
594 		}
595 	case FUSE_MKDIR:
596 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
597 			"Missing request body";
598 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
599 			"Missing request filename";
600 		// No redundant information for checking buflen
601 		break;
602 	case FUSE_RENAME:
603 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
604 			"Missing request body";
605 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
606 			"Missing request filename";
607 		// No redundant information for checking buflen
608 		break;
609 	case FUSE_LINK:
610 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
611 			"Missing request body";
612 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
613 			"Missing request filename";
614 		// No redundant information for checking buflen
615 		break;
616 	case FUSE_OPEN:
617 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
618 		EXPECT_EQ((size_t)buflen, inlen);
619 		break;
620 	case FUSE_READ:
621 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
622 		EXPECT_EQ((size_t)buflen, inlen);
623 		break;
624 	case FUSE_WRITE:
625 		{
626 			size_t s;
627 
628 			if (m_kernel_minor_version >= 9)
629 				s = sizeof(in.body.write);
630 			else
631 				s = FUSE_COMPAT_WRITE_IN_SIZE;
632 			// I suppose a 0-byte write should be allowed
633 			EXPECT_GE(inlen, fih + s) << "Missing request body";
634 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
635 			break;
636 		}
637 	case FUSE_DESTROY:
638 	case FUSE_STATFS:
639 		EXPECT_EQ(inlen, fih);
640 		EXPECT_EQ((size_t)buflen, inlen);
641 		break;
642 	case FUSE_RELEASE:
643 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
644 		EXPECT_EQ((size_t)buflen, inlen);
645 		break;
646 	case FUSE_FSYNC:
647 	case FUSE_FSYNCDIR:
648 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
649 		EXPECT_EQ((size_t)buflen, inlen);
650 		break;
651 	case FUSE_SETXATTR:
652 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
653 			"Missing request body";
654 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
655 			"Missing request attribute name";
656 		// No redundant information for checking buflen
657 		break;
658 	case FUSE_GETXATTR:
659 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
660 			"Missing request body";
661 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
662 			"Missing request attribute name";
663 		// No redundant information for checking buflen
664 		break;
665 	case FUSE_LISTXATTR:
666 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
667 		EXPECT_EQ((size_t)buflen, inlen);
668 		break;
669 	case FUSE_REMOVEXATTR:
670 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
671 		// No redundant information for checking buflen
672 		break;
673 	case FUSE_FLUSH:
674 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
675 		EXPECT_EQ((size_t)buflen, inlen);
676 		break;
677 	case FUSE_INIT:
678 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
679 		EXPECT_EQ((size_t)buflen, inlen);
680 		break;
681 	case FUSE_OPENDIR:
682 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
683 		EXPECT_EQ((size_t)buflen, inlen);
684 		break;
685 	case FUSE_READDIR:
686 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
687 		EXPECT_EQ((size_t)buflen, inlen);
688 		break;
689 	case FUSE_RELEASEDIR:
690 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
691 		EXPECT_EQ((size_t)buflen, inlen);
692 		break;
693 	case FUSE_GETLK:
694 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
695 		EXPECT_EQ((size_t)buflen, inlen);
696 		break;
697 	case FUSE_SETLK:
698 	case FUSE_SETLKW:
699 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
700 		EXPECT_EQ((size_t)buflen, inlen);
701 		break;
702 	case FUSE_ACCESS:
703 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
704 		EXPECT_EQ((size_t)buflen, inlen);
705 		break;
706 	case FUSE_CREATE:
707 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
708 			"Missing request body";
709 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
710 			"Missing request filename";
711 		// No redundant information for checking buflen
712 		break;
713 	case FUSE_INTERRUPT:
714 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
715 		EXPECT_EQ((size_t)buflen, inlen);
716 		break;
717 	case FUSE_FALLOCATE:
718 		EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
719 		EXPECT_EQ((size_t)buflen, inlen);
720 		break;
721 	case FUSE_BMAP:
722 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
723 		EXPECT_EQ((size_t)buflen, inlen);
724 		break;
725 	case FUSE_LSEEK:
726 		EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
727 		EXPECT_EQ((size_t)buflen, inlen);
728 		break;
729 	case FUSE_COPY_FILE_RANGE:
730 		EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
731 		EXPECT_EQ(0ul, in.body.copy_file_range.flags);
732 		EXPECT_EQ((size_t)buflen, inlen);
733 		break;
734 	case FUSE_NOTIFY_REPLY:
735 	case FUSE_BATCH_FORGET:
736 	case FUSE_IOCTL:
737 	case FUSE_POLL:
738 	case FUSE_READDIRPLUS:
739 		FAIL() << "Unsupported opcode?";
740 	default:
741 		FAIL() << "Unknown opcode " << in.header.opcode;
742 	}
743 	/* Verify that the ticket's unique value is actually unique. */
744 	if (m_uniques->find(in.header.unique) != m_uniques->end())
745 		FAIL() << "Non-unique \"unique\" value";
746 	m_uniques->insert(in.header.unique);
747 }
748 
init(uint32_t flags)749 void MockFS::init(uint32_t flags) {
750 	ssize_t buflen;
751 
752 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
753 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
754 
755 	read_request(*in, buflen);
756 	if (verbosity > 0)
757 		debug_request(*in, buflen);
758 	audit_request(*in, buflen);
759 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
760 
761 	out->header.unique = in->header.unique;
762 	out->header.error = 0;
763 	out->body.init.major = FUSE_KERNEL_VERSION;
764 	out->body.init.minor = m_kernel_minor_version;;
765 	out->body.init.flags = in->body.init.flags & flags;
766 	out->body.init.max_write = m_maxwrite;
767 	out->body.init.max_readahead = m_maxreadahead;
768 
769 	if (m_kernel_minor_version < 23) {
770 		SET_OUT_HEADER_LEN(*out, init_7_22);
771 	} else {
772 		out->body.init.time_gran = m_time_gran;
773 		SET_OUT_HEADER_LEN(*out, init);
774 	}
775 
776 	write(m_fuse_fd, out.get(), out->header.len);
777 }
778 
kill_daemon()779 void MockFS::kill_daemon() {
780 	m_quit = true;
781 	if (m_daemon_id != NULL)
782 		pthread_kill(m_daemon_id, SIGUSR1);
783 	// Closing the /dev/fuse file descriptor first allows unmount to
784 	// succeed even if the daemon doesn't correctly respond to commands
785 	// during the unmount sequence.
786 	close(m_fuse_fd);
787 	m_fuse_fd = -1;
788 }
789 
join_daemon()790 void MockFS::join_daemon() {
791 	if (m_daemon_id != NULL) {
792 		pthread_join(m_daemon_id, NULL);
793 		m_daemon_id = NULL;
794 	}
795 }
796 
loop()797 void MockFS::loop() {
798 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
799 
800 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
801 	ASSERT_TRUE(in != NULL);
802 	while (!m_quit) {
803 		ssize_t buflen;
804 
805 		bzero(in.get(), sizeof(*in));
806 		read_request(*in, buflen);
807 		if (m_quit)
808 			break;
809 		if (verbosity > 0)
810 			debug_request(*in, buflen);
811 		audit_request(*in, buflen);
812 		if (pid_ok((pid_t)in->header.pid)) {
813 			process(*in, out);
814 		} else {
815 			/*
816 			 * Reject any requests from unknown processes.  Because
817 			 * we actually do mount a filesystem, plenty of
818 			 * unrelated system daemons may try to access it.
819 			 */
820 			if (verbosity > 1)
821 				printf("\tREJECTED (wrong pid %d)\n",
822 					in->header.pid);
823 			process_default(*in, out);
824 		}
825 		for (auto &it: out)
826 			write_response(*it);
827 		out.clear();
828 	}
829 }
830 
notify_inval_entry(ino_t parent,const char * name,size_t namelen,int expected_errno)831 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen,
832 		int expected_errno)
833 {
834 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
835 
836 	out->expected_errno = expected_errno;
837 	out->header.unique = 0;	/* 0 means asynchronous notification */
838 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
839 	out->body.inval_entry.parent = parent;
840 	out->body.inval_entry.namelen = namelen;
841 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
842 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
843 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
844 		namelen;
845 	debug_response(*out);
846 	write_response(*out);
847 	return 0;
848 }
849 
notify_inval_inode(ino_t ino,off_t off,ssize_t len)850 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
851 {
852 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
853 
854 	out->header.unique = 0;	/* 0 means asynchronous notification */
855 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
856 	out->body.inval_inode.ino = ino;
857 	out->body.inval_inode.off = off;
858 	out->body.inval_inode.len = len;
859 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
860 	debug_response(*out);
861 	write_response(*out);
862 	return 0;
863 }
864 
notify_store(ino_t ino,off_t off,const void * data,ssize_t size)865 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
866 {
867 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
868 
869 	out->header.unique = 0;	/* 0 means asynchronous notification */
870 	out->header.error = FUSE_NOTIFY_STORE;
871 	out->body.store.nodeid = ino;
872 	out->body.store.offset = off;
873 	out->body.store.size = size;
874 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
875 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
876 	debug_response(*out);
877 	write_response(*out);
878 	return 0;
879 }
880 
pid_ok(pid_t pid)881 bool MockFS::pid_ok(pid_t pid) {
882 	if (pid == m_pid) {
883 		return (true);
884 	} else if (pid == m_child_pid) {
885 		return (true);
886 	} else {
887 		struct kinfo_proc *ki;
888 		bool ok = false;
889 
890 		ki = kinfo_getproc(pid);
891 		if (ki == NULL)
892 			return (false);
893 		/*
894 		 * Allow access by the aio daemon processes so that our tests
895 		 * can use aio functions
896 		 */
897 		if (0 == strncmp("aiod", ki->ki_comm, 4))
898 			ok = true;
899 		free(ki);
900 		return (ok);
901 	}
902 }
903 
process_default(const mockfs_buf_in & in,std::vector<std::unique_ptr<mockfs_buf_out>> & out)904 void MockFS::process_default(const mockfs_buf_in& in,
905 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
906 {
907 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
908 	out0->header.unique = in.header.unique;
909 	out0->header.error = -EOPNOTSUPP;
910 	out0->header.len = sizeof(out0->header);
911 	out.push_back(std::move(out0));
912 }
913 
read_request(mockfs_buf_in & in,ssize_t & res)914 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
915 	int nready = 0;
916 	fd_set readfds;
917 	pollfd fds[1];
918 	struct kevent changes[1];
919 	struct kevent events[1];
920 	struct timespec timeout_ts;
921 	struct timeval timeout_tv;
922 	const int timeout_ms = 999;
923 	int timeout_int, nfds;
924 	int fuse_fd;
925 
926 	switch (m_pm) {
927 	case BLOCKING:
928 		break;
929 	case KQ:
930 		timeout_ts.tv_sec = 0;
931 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
932 		while (nready == 0) {
933 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
934 				EV_ADD | EV_ONESHOT, 0, 0, 0);
935 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
936 				&timeout_ts);
937 			if (m_quit)
938 				return;
939 		}
940 		ASSERT_LE(0, nready) << strerror(errno);
941 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
942 		if (events[0].flags & EV_ERROR)
943 			FAIL() << strerror(events[0].data);
944 		else if (events[0].flags & EV_EOF)
945 			FAIL() << strerror(events[0].fflags);
946 		m_nready = events[0].data;
947 		break;
948 	case POLL:
949 		timeout_int = timeout_ms;
950 		fds[0].fd = m_fuse_fd;
951 		fds[0].events = POLLIN;
952 		while (nready == 0) {
953 			nready = poll(fds, 1, timeout_int);
954 			if (m_quit)
955 				return;
956 		}
957 		ASSERT_LE(0, nready) << strerror(errno);
958 		ASSERT_TRUE(fds[0].revents & POLLIN);
959 		break;
960 	case SELECT:
961 		fuse_fd = m_fuse_fd;
962 		if (fuse_fd < 0)
963 			break;
964 		timeout_tv.tv_sec = 0;
965 		timeout_tv.tv_usec = timeout_ms * 1'000;
966 		nfds = fuse_fd + 1;
967 		while (nready == 0) {
968 			FD_ZERO(&readfds);
969 			FD_SET(fuse_fd, &readfds);
970 			nready = select(nfds, &readfds, NULL, NULL,
971 				&timeout_tv);
972 			if (m_quit)
973 				return;
974 		}
975 		ASSERT_LE(0, nready) << strerror(errno);
976 		ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
977 		break;
978 	default:
979 		FAIL() << "not yet implemented";
980 	}
981 	res = read(m_fuse_fd, &in, sizeof(in));
982 
983 	if (res < 0 && errno != EBADF && !m_quit && !m_expect_unmount) {
984 		m_quit = true;
985 		FAIL() << "read: " << strerror(errno);
986 	}
987 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
988 	/*
989 	 * Inconsistently, fuse_in_header.len is the size of the entire
990 	 * request,including header, even though fuse_out_header.len excludes
991 	 * the size of the header.
992 	 */
993 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
994 }
995 
write_response(const mockfs_buf_out & out)996 void MockFS::write_response(const mockfs_buf_out &out) {
997 	fd_set writefds;
998 	pollfd fds[1];
999 	struct kevent changes[1];
1000 	struct kevent events[1];
1001 	int nready, nfds;
1002 	ssize_t r;
1003 
1004 	switch (m_pm) {
1005 	case BLOCKING:
1006 		break;
1007 	case KQ:
1008 		EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1009 			EV_ADD | EV_ONESHOT, 0, 0, 0);
1010 		nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1011 			NULL);
1012 		ASSERT_LE(0, nready) << strerror(errno);
1013 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1014 		if (events[0].flags & EV_ERROR)
1015 			FAIL() << strerror(events[0].data);
1016 		else if (events[0].flags & EV_EOF)
1017 			FAIL() << strerror(events[0].fflags);
1018 		m_nready = events[0].data;
1019 		break;
1020 	case POLL:
1021 		fds[0].fd = m_fuse_fd;
1022 		fds[0].events = POLLOUT;
1023 		nready = poll(fds, 1, INFTIM);
1024 		ASSERT_LE(0, nready) << strerror(errno);
1025 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
1026 		ASSERT_TRUE(fds[0].revents & POLLOUT);
1027 		break;
1028 	case SELECT:
1029 		FD_ZERO(&writefds);
1030 		FD_SET(m_fuse_fd, &writefds);
1031 		nfds = m_fuse_fd + 1;
1032 		nready = select(nfds, NULL, &writefds, NULL, NULL);
1033 		ASSERT_LE(0, nready) << strerror(errno);
1034 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
1035 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1036 		break;
1037 	default:
1038 		FAIL() << "not yet implemented";
1039 	}
1040 	r = write(m_fuse_fd, &out, out.header.len);
1041 	if (out.expected_errno) {
1042 		ASSERT_EQ(-1, r);
1043 		ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1044 	} else {
1045 		if (r <= 0 && errno == EINVAL) {
1046 			printf("Failed to write response.  unique=%" PRIu64
1047 			    ":\n", out.header.unique);
1048 		}
1049 		ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1050 	}
1051 }
1052 
service(void * pthr_data)1053 void* MockFS::service(void *pthr_data) {
1054 	MockFS *mock_fs = (MockFS*)pthr_data;
1055 
1056 	mock_fs->loop();
1057 
1058 	return (NULL);
1059 }
1060 
unmount()1061 void MockFS::unmount() {
1062 	::unmount("mountpoint", 0);
1063 }
1064