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