xref: /freebsd/tests/sys/fs/fusefs/utils.cc (revision 86aa9539fef591a363b06a0ebd3aa7a07f4c1579)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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  * $FreeBSD$
31  */
32 
33 extern "C" {
34 #include <sys/param.h>
35 #include <sys/mman.h>
36 #include <sys/module.h>
37 #include <sys/sysctl.h>
38 #include <sys/wait.h>
39 
40 #include <dirent.h>
41 #include <fcntl.h>
42 #include <grp.h>
43 #include <pwd.h>
44 #include <semaphore.h>
45 #include <unistd.h>
46 }
47 
48 #include <gtest/gtest.h>
49 
50 #include "mockfs.hh"
51 #include "utils.hh"
52 
53 using namespace testing;
54 
55 /*
56  * The default max_write is set to this formula in libfuse, though
57  * individual filesystems can lower it.  The "- 4096" was added in
58  * commit 154ffe2, with the commit message "fix".
59  */
60 const uint32_t libfuse_max_write = 32 * getpagesize() + 0x1000 - 4096;
61 
62 /*
63  * Set the default max_write to a distinct value from MAXPHYS to catch bugs
64  * that confuse the two.
65  */
66 const uint32_t default_max_write = MIN(libfuse_max_write, MAXPHYS / 2);
67 
68 
69 /* Check that fusefs(4) is accessible and the current user can mount(2) */
70 void check_environment()
71 {
72 	const char *devnode = "/dev/fuse";
73 	const char *usermount_node = "vfs.usermount";
74 	int usermount_val = 0;
75 	size_t usermount_size = sizeof(usermount_val);
76 	if (eaccess(devnode, R_OK | W_OK)) {
77 		if (errno == ENOENT) {
78 			GTEST_SKIP() << devnode << " does not exist";
79 		} else if (errno == EACCES) {
80 			GTEST_SKIP() << devnode <<
81 			    " is not accessible by the current user";
82 		} else {
83 			GTEST_SKIP() << strerror(errno);
84 		}
85 	}
86 	sysctlbyname(usermount_node, &usermount_val, &usermount_size,
87 		     NULL, 0);
88 	if (geteuid() != 0 && !usermount_val)
89 		GTEST_SKIP() << "current user is not allowed to mount";
90 }
91 
92 bool is_unsafe_aio_enabled(void) {
93 	const char *node = "vfs.aio.enable_unsafe";
94 	int val = 0;
95 	size_t size = sizeof(val);
96 
97 	if (sysctlbyname(node, &val, &size, NULL, 0)) {
98 		perror("sysctlbyname");
99 		return (false);
100 	}
101 	return (val != 0);
102 }
103 
104 class FuseEnv: public Environment {
105 	virtual void SetUp() {
106 	}
107 };
108 
109 void FuseTest::SetUp() {
110 	const char *maxbcachebuf_node = "vfs.maxbcachebuf";
111 	const char *maxphys_node = "kern.maxphys";
112 	int val = 0;
113 	size_t size = sizeof(val);
114 
115 	/*
116 	 * XXX check_environment should be called from FuseEnv::SetUp, but
117 	 * can't due to https://github.com/google/googletest/issues/2189
118 	 */
119 	check_environment();
120 	if (IsSkipped())
121 		return;
122 
123 	ASSERT_EQ(0, sysctlbyname(maxbcachebuf_node, &val, &size, NULL, 0))
124 		<< strerror(errno);
125 	m_maxbcachebuf = val;
126 	ASSERT_EQ(0, sysctlbyname(maxphys_node, &val, &size, NULL, 0))
127 		<< strerror(errno);
128 	m_maxphys = val;
129 
130 	try {
131 		m_mock = new MockFS(m_maxreadahead, m_allow_other,
132 			m_default_permissions, m_push_symlinks_in, m_ro,
133 			m_pm, m_init_flags, m_kernel_minor_version,
134 			m_maxwrite, m_async, m_noclusterr, m_time_gran,
135 			m_nointr);
136 		/*
137 		 * FUSE_ACCESS is called almost universally.  Expecting it in
138 		 * each test case would be super-annoying.  Instead, set a
139 		 * default expectation for FUSE_ACCESS and return ENOSYS.
140 		 *
141 		 * Individual test cases can override this expectation since
142 		 * googlemock evaluates expectations in LIFO order.
143 		 */
144 		EXPECT_CALL(*m_mock, process(
145 			ResultOf([=](auto in) {
146 				return (in.header.opcode == FUSE_ACCESS);
147 			}, Eq(true)),
148 			_)
149 		).Times(AnyNumber())
150 		.WillRepeatedly(Invoke(ReturnErrno(ENOSYS)));
151 		/*
152 		 * FUSE_BMAP is called for most test cases that read data.  Set
153 		 * a default expectation and return ENOSYS.
154 		 *
155 		 * Individual test cases can override this expectation since
156 		 * googlemock evaluates expectations in LIFO order.
157 		 */
158 		EXPECT_CALL(*m_mock, process(
159 			ResultOf([=](auto in) {
160 				return (in.header.opcode == FUSE_BMAP);
161 			}, Eq(true)),
162 			_)
163 		).Times(AnyNumber())
164 		.WillRepeatedly(Invoke(ReturnErrno(ENOSYS)));
165 	} catch (std::system_error err) {
166 		FAIL() << err.what();
167 	}
168 }
169 
170 void
171 FuseTest::expect_access(uint64_t ino, mode_t access_mode, int error)
172 {
173 	EXPECT_CALL(*m_mock, process(
174 		ResultOf([=](auto in) {
175 			return (in.header.opcode == FUSE_ACCESS &&
176 				in.header.nodeid == ino &&
177 				in.body.access.mask == access_mode);
178 		}, Eq(true)),
179 		_)
180 	).WillOnce(Invoke(ReturnErrno(error)));
181 }
182 
183 void
184 FuseTest::expect_destroy(int error)
185 {
186 	EXPECT_CALL(*m_mock, process(
187 		ResultOf([=](auto in) {
188 			return (in.header.opcode == FUSE_DESTROY);
189 		}, Eq(true)),
190 		_)
191 	).WillOnce(Invoke( ReturnImmediate([&](auto in, auto& out) {
192 		m_mock->m_quit = true;
193 		out.header.len = sizeof(out.header);
194 		out.header.unique = in.header.unique;
195 		out.header.error = -error;
196 	})));
197 }
198 
199 void
200 FuseTest::expect_flush(uint64_t ino, int times, ProcessMockerT r)
201 {
202 	EXPECT_CALL(*m_mock, process(
203 		ResultOf([=](auto in) {
204 			return (in.header.opcode == FUSE_FLUSH &&
205 				in.header.nodeid == ino);
206 		}, Eq(true)),
207 		_)
208 	).Times(times)
209 	.WillRepeatedly(Invoke(r));
210 }
211 
212 void
213 FuseTest::expect_forget(uint64_t ino, uint64_t nlookup, sem_t *sem)
214 {
215 	EXPECT_CALL(*m_mock, process(
216 		ResultOf([=](auto in) {
217 			return (in.header.opcode == FUSE_FORGET &&
218 				in.header.nodeid == ino &&
219 				in.body.forget.nlookup == nlookup);
220 		}, Eq(true)),
221 		_)
222 	).WillOnce(Invoke([=](auto in __unused, auto &out __unused) {
223 		if (sem != NULL)
224 			sem_post(sem);
225 		/* FUSE_FORGET has no response! */
226 	}));
227 }
228 
229 void FuseTest::expect_getattr(uint64_t ino, uint64_t size)
230 {
231 	EXPECT_CALL(*m_mock, process(
232 		ResultOf([=](auto in) {
233 			return (in.header.opcode == FUSE_GETATTR &&
234 				in.header.nodeid == ino);
235 		}, Eq(true)),
236 		_)
237 	).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
238 		SET_OUT_HEADER_LEN(out, attr);
239 		out.body.attr.attr.ino = ino;	// Must match nodeid
240 		out.body.attr.attr.mode = S_IFREG | 0644;
241 		out.body.attr.attr.size = size;
242 		out.body.attr.attr_valid = UINT64_MAX;
243 	})));
244 }
245 
246 void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
247 	uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
248 {
249 	EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
250 	.Times(times)
251 	.WillRepeatedly(Invoke(
252 		ReturnImmediate([=](auto in __unused, auto& out) {
253 		SET_OUT_HEADER_LEN(out, entry);
254 		out.body.entry.attr.mode = mode;
255 		out.body.entry.nodeid = ino;
256 		out.body.entry.attr.nlink = 1;
257 		out.body.entry.attr_valid = attr_valid;
258 		out.body.entry.attr.size = size;
259 		out.body.entry.attr.uid = uid;
260 		out.body.entry.attr.gid = gid;
261 	})));
262 }
263 
264 void FuseTest::expect_lookup_7_8(const char *relpath, uint64_t ino, mode_t mode,
265 	uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
266 {
267 	EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
268 	.Times(times)
269 	.WillRepeatedly(Invoke(
270 		ReturnImmediate([=](auto in __unused, auto& out) {
271 		SET_OUT_HEADER_LEN(out, entry_7_8);
272 		out.body.entry.attr.mode = mode;
273 		out.body.entry.nodeid = ino;
274 		out.body.entry.attr.nlink = 1;
275 		out.body.entry.attr_valid = attr_valid;
276 		out.body.entry.attr.size = size;
277 		out.body.entry.attr.uid = uid;
278 		out.body.entry.attr.gid = gid;
279 	})));
280 }
281 
282 void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
283 {
284 	EXPECT_CALL(*m_mock, process(
285 		ResultOf([=](auto in) {
286 			return (in.header.opcode == FUSE_OPEN &&
287 				in.header.nodeid == ino);
288 		}, Eq(true)),
289 		_)
290 	).Times(times)
291 	.WillRepeatedly(Invoke(
292 		ReturnImmediate([=](auto in __unused, auto& out) {
293 		out.header.len = sizeof(out.header);
294 		SET_OUT_HEADER_LEN(out, open);
295 		out.body.open.fh = FH;
296 		out.body.open.open_flags = flags;
297 	})));
298 }
299 
300 void FuseTest::expect_opendir(uint64_t ino)
301 {
302 	/* opendir(3) calls fstatfs */
303 	EXPECT_CALL(*m_mock, process(
304 		ResultOf([](auto in) {
305 			return (in.header.opcode == FUSE_STATFS);
306 		}, Eq(true)),
307 		_)
308 	).WillRepeatedly(Invoke(
309 	ReturnImmediate([=](auto i __unused, auto& out) {
310 		SET_OUT_HEADER_LEN(out, statfs);
311 	})));
312 
313 	EXPECT_CALL(*m_mock, process(
314 		ResultOf([=](auto in) {
315 			return (in.header.opcode == FUSE_OPENDIR &&
316 				in.header.nodeid == ino);
317 		}, Eq(true)),
318 		_)
319 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
320 		out.header.len = sizeof(out.header);
321 		SET_OUT_HEADER_LEN(out, open);
322 		out.body.open.fh = FH;
323 	})));
324 }
325 
326 void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
327 	uint64_t osize, const void *contents, int flags)
328 {
329 	EXPECT_CALL(*m_mock, process(
330 		ResultOf([=](auto in) {
331 			return (in.header.opcode == FUSE_READ &&
332 				in.header.nodeid == ino &&
333 				in.body.read.fh == FH &&
334 				in.body.read.offset == offset &&
335 				in.body.read.size == isize &&
336 				flags == -1 ?
337 					(in.body.read.flags == O_RDONLY ||
338 					 in.body.read.flags == O_RDWR)
339 				: in.body.read.flags == (uint32_t)flags);
340 		}, Eq(true)),
341 		_)
342 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
343 		out.header.len = sizeof(struct fuse_out_header) + osize;
344 		memmove(out.body.bytes, contents, osize);
345 	}))).RetiresOnSaturation();
346 }
347 
348 void FuseTest::expect_readdir(uint64_t ino, uint64_t off,
349 	std::vector<struct dirent> &ents)
350 {
351 	EXPECT_CALL(*m_mock, process(
352 		ResultOf([=](auto in) {
353 			return (in.header.opcode == FUSE_READDIR &&
354 				in.header.nodeid == ino &&
355 				in.body.readdir.fh == FH &&
356 				in.body.readdir.offset == off);
357 		}, Eq(true)),
358 		_)
359 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
360 		struct fuse_dirent *fde = (struct fuse_dirent*)&(out.body);
361 		int i = 0;
362 
363 		out.header.error = 0;
364 		out.header.len = 0;
365 
366 		for (const auto& it: ents) {
367 			size_t entlen, entsize;
368 
369 			fde->ino = it.d_fileno;
370 			fde->off = it.d_off;
371 			fde->type = it.d_type;
372 			fde->namelen = it.d_namlen;
373 			strncpy(fde->name, it.d_name, it.d_namlen);
374 			entlen = FUSE_NAME_OFFSET + fde->namelen;
375 			entsize = FUSE_DIRENT_SIZE(fde);
376 			/*
377 			 * The FUSE protocol does not require zeroing out the
378 			 * unused portion of the name.  But it's a good
379 			 * practice to prevent information disclosure to the
380 			 * FUSE client, even though the client is usually the
381 			 * kernel
382 			 */
383 			memset(fde->name + fde->namelen, 0, entsize - entlen);
384 			if (out.header.len + entsize > in.body.read.size) {
385 				printf("Overflow in readdir expectation: i=%d\n"
386 					, i);
387 				break;
388 			}
389 			out.header.len += entsize;
390 			fde = (struct fuse_dirent*)
391 				((intmax_t*)fde + entsize / sizeof(intmax_t));
392 			i++;
393 		}
394 		out.header.len += sizeof(out.header);
395 	})));
396 
397 }
398 void FuseTest::expect_release(uint64_t ino, uint64_t fh)
399 {
400 	EXPECT_CALL(*m_mock, process(
401 		ResultOf([=](auto in) {
402 			return (in.header.opcode == FUSE_RELEASE &&
403 				in.header.nodeid == ino &&
404 				in.body.release.fh == fh);
405 		}, Eq(true)),
406 		_)
407 	).WillOnce(Invoke(ReturnErrno(0)));
408 }
409 
410 void FuseTest::expect_releasedir(uint64_t ino, ProcessMockerT r)
411 {
412 	EXPECT_CALL(*m_mock, process(
413 		ResultOf([=](auto in) {
414 			return (in.header.opcode == FUSE_RELEASEDIR &&
415 				in.header.nodeid == ino &&
416 				in.body.release.fh == FH);
417 		}, Eq(true)),
418 		_)
419 	).WillOnce(Invoke(r));
420 }
421 
422 void FuseTest::expect_unlink(uint64_t parent, const char *path, int error)
423 {
424 	EXPECT_CALL(*m_mock, process(
425 		ResultOf([=](auto in) {
426 			return (in.header.opcode == FUSE_UNLINK &&
427 				0 == strcmp(path, in.body.unlink) &&
428 				in.header.nodeid == parent);
429 		}, Eq(true)),
430 		_)
431 	).WillOnce(Invoke(ReturnErrno(error)));
432 }
433 
434 void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
435 	uint64_t osize, uint32_t flags_set, uint32_t flags_unset,
436 	const void *contents)
437 {
438 	EXPECT_CALL(*m_mock, process(
439 		ResultOf([=](auto in) {
440 			const char *buf = (const char*)in.body.bytes +
441 				sizeof(struct fuse_write_in);
442 			bool pid_ok;
443 			uint32_t wf = in.body.write.write_flags;
444 
445 			if (wf & FUSE_WRITE_CACHE)
446 				pid_ok = true;
447 			else
448 				pid_ok = (pid_t)in.header.pid == getpid();
449 
450 			return (in.header.opcode == FUSE_WRITE &&
451 				in.header.nodeid == ino &&
452 				in.body.write.fh == FH &&
453 				in.body.write.offset == offset  &&
454 				in.body.write.size == isize &&
455 				pid_ok &&
456 				(wf & flags_set) == flags_set &&
457 				(wf & flags_unset) == 0 &&
458 				(in.body.write.flags == O_WRONLY ||
459 				 in.body.write.flags == O_RDWR) &&
460 				0 == bcmp(buf, contents, isize));
461 		}, Eq(true)),
462 		_)
463 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
464 		SET_OUT_HEADER_LEN(out, write);
465 		out.body.write.size = osize;
466 	})));
467 }
468 
469 void FuseTest::expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
470 	uint64_t osize, const void *contents)
471 {
472 	EXPECT_CALL(*m_mock, process(
473 		ResultOf([=](auto in) {
474 			const char *buf = (const char*)in.body.bytes +
475 				FUSE_COMPAT_WRITE_IN_SIZE;
476 			bool pid_ok = (pid_t)in.header.pid == getpid();
477 			return (in.header.opcode == FUSE_WRITE &&
478 				in.header.nodeid == ino &&
479 				in.body.write.fh == FH &&
480 				in.body.write.offset == offset  &&
481 				in.body.write.size == isize &&
482 				pid_ok &&
483 				0 == bcmp(buf, contents, isize));
484 		}, Eq(true)),
485 		_)
486 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
487 		SET_OUT_HEADER_LEN(out, write);
488 		out.body.write.size = osize;
489 	})));
490 }
491 
492 void
493 get_unprivileged_id(uid_t *uid, gid_t *gid)
494 {
495 	struct passwd *pw;
496 	struct group *gr;
497 
498 	/*
499 	 * First try "tests", Kyua's default unprivileged user.  XXX after
500 	 * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API
501 	 */
502 	pw = getpwnam("tests");
503 	if (pw == NULL) {
504 		/* Fall back to "nobody" */
505 		pw = getpwnam("nobody");
506 	}
507 	if (pw == NULL)
508 		GTEST_SKIP() << "Test requires an unprivileged user";
509 	/* Use group "nobody", which is Kyua's default unprivileged group */
510 	gr = getgrnam("nobody");
511 	if (gr == NULL)
512 		GTEST_SKIP() << "Test requires an unprivileged group";
513 	*uid = pw->pw_uid;
514 	*gid = gr->gr_gid;
515 }
516 
517 void
518 FuseTest::fork(bool drop_privs, int *child_status,
519 	std::function<void()> parent_func,
520 	std::function<int()> child_func)
521 {
522 	sem_t *sem;
523 	int mprot = PROT_READ | PROT_WRITE;
524 	int mflags = MAP_ANON | MAP_SHARED;
525 	pid_t child;
526 	uid_t uid;
527 	gid_t gid;
528 
529 	if (drop_privs) {
530 		get_unprivileged_id(&uid, &gid);
531 		if (IsSkipped())
532 			return;
533 	}
534 
535 	sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0);
536 	ASSERT_NE(MAP_FAILED, sem) << strerror(errno);
537 	ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno);
538 
539 	if ((child = ::fork()) == 0) {
540 		/* In child */
541 		int err = 0;
542 
543 		if (sem_wait(sem)) {
544 			perror("sem_wait");
545 			err = 1;
546 			goto out;
547 		}
548 
549 		if (drop_privs && 0 != setegid(gid)) {
550 			perror("setegid");
551 			err = 1;
552 			goto out;
553 		}
554 		if (drop_privs && 0 != setreuid(-1, uid)) {
555 			perror("setreuid");
556 			err = 1;
557 			goto out;
558 		}
559 		err = child_func();
560 
561 out:
562 		sem_destroy(sem);
563 		_exit(err);
564 	} else if (child > 0) {
565 		/*
566 		 * In parent.  Cleanup must happen here, because it's still
567 		 * privileged.
568 		 */
569 		m_mock->m_child_pid = child;
570 		ASSERT_NO_FATAL_FAILURE(parent_func());
571 
572 		/* Signal the child process to go */
573 		ASSERT_EQ(0, sem_post(sem)) << strerror(errno);
574 
575 		ASSERT_LE(0, wait(child_status)) << strerror(errno);
576 	} else {
577 		FAIL() << strerror(errno);
578 	}
579 	munmap(sem, sizeof(*sem));
580 	return;
581 }
582 
583 static void usage(char* progname) {
584 	fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname);
585 	exit(2);
586 }
587 
588 int main(int argc, char **argv) {
589 	int ch;
590 	FuseEnv *fuse_env = new FuseEnv;
591 
592 	InitGoogleTest(&argc, argv);
593 	AddGlobalTestEnvironment(fuse_env);
594 
595 	while ((ch = getopt(argc, argv, "v")) != -1) {
596 		switch (ch) {
597 			case 'v':
598 				verbosity++;
599 				break;
600 			default:
601 				usage(argv[0]);
602 				break;
603 		}
604 	}
605 
606 	return (RUN_ALL_TESTS());
607 }
608