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