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