Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 03 Sep 2019 14:05:49 -0000
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r345624 - projects/fuse2/tests/sys/fs/fusefs
Message-ID:  <201903280112.x2S1Ci3S069607@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: asomers
Date: Thu Mar 28 01:12:44 2019
New Revision: 345624
URL: https://svnweb.freebsd.org/changeset/base/345624

Log:
  fusefs: deduplicate code in the allow_other test
  
  Sponsored by:	The FreeBSD Foundation

Modified:
  projects/fuse2/tests/sys/fs/fusefs/allow_other.cc
  projects/fuse2/tests/sys/fs/fusefs/utils.cc
  projects/fuse2/tests/sys/fs/fusefs/utils.hh

Modified: projects/fuse2/tests/sys/fs/fusefs/allow_other.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/allow_other.cc	Thu Mar 28 00:25:57 2019	(r345623)
+++ projects/fuse2/tests/sys/fs/fusefs/allow_other.cc	Thu Mar 28 01:12:44 2019	(r345624)
@@ -35,11 +35,8 @@
 
 extern "C" {
 #include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/wait.h>
 #include <fcntl.h>
-#include <pwd.h>
-#include <semaphore.h>
+#include <unistd.h>
 }
 
 #include "mockfs.hh"
@@ -47,27 +44,9 @@ extern "C" {
 
 using namespace testing;
 
-void sighandler(int __unused sig) {}
+const static char FULLPATH[] = "mountpoint/some_file.txt";
+const static char RELPATH[] = "some_file.txt";
 
-static void
-get_unprivileged_uid(int *uid)
-{
-	struct passwd *pw;
-
-	/* 
-	 * First try "tests", Kyua's default unprivileged user.  XXX after
-	 * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API
-	 */
-	pw = getpwnam("tests");
-	if (pw == NULL) {
-		/* Fall back to "nobody" */
-		pw = getpwnam("nobody");
-	}
-	if (pw == NULL)
-		GTEST_SKIP() << "Test requires an unprivileged user";
-	*uid = pw->pw_uid;
-}
-
 class NoAllowOther: public FuseTest {
 
 public:
@@ -78,9 +57,6 @@ virtual void SetUp() {
 	if (geteuid() != 0) {
 		GTEST_SKIP() << "This test must be run as root";
 	}
-	get_unprivileged_uid(&m_uid);
-	if (IsSkipped())
-		return;
 
 	FuseTest::SetUp();
 }
@@ -97,119 +73,42 @@ virtual void SetUp() {
 
 TEST_F(AllowOther, allowed)
 {
-	const char FULLPATH[] = "mountpoint/some_file.txt";
-	const char RELPATH[] = "some_file.txt";
-	uint64_t ino = 42;
-	int fd;
-	pid_t child;
-	sem_t *sem;
-	int mprot = PROT_READ | PROT_WRITE;
-	int mflags = MAP_ANON | MAP_SHARED;
-	
-	sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0);
-	ASSERT_NE(MAP_FAILED, sem) << strerror(errno);
-	ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno);
+	fork(true, [&] {
+			uint64_t ino = 42;
 
-	if ((child = fork()) == 0) {
-		/* In child */
-		int err = 0;
+			expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+			expect_open(ino, 0, 1);
+			expect_release(ino);
+			expect_getattr(ino, 0);
+		}, []() {
+			int fd;
 
-		ASSERT_EQ(0, sem_wait(sem)) << strerror(errno);
-
-		/* Drop privileges before accessing */
-		if (0 != setreuid(-1, m_uid)) {
-			perror("setreuid");
-			err = 1;
-			goto out;
+			fd = open(FULLPATH, O_RDONLY);
+			if (fd < 0) {
+				perror("open");
+				return(1);
+			}
+			return 0;
 		}
-		fd = open(FULLPATH, O_RDONLY);
-		if (fd < 0) {
-			perror("open");
-			err = 1;
-		}
-
-out:
-		sem_destroy(sem);
-		_exit(err);
-		/* Deliberately leak fd */
-	} else if (child > 0) {
-		int child_status;
-
-		/* 
-		 * In parent.  Cleanup must happen here, because it's still
-		 * privileged.
-		 */
-		expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
-		expect_open(ino, 0, 1);
-		expect_release(ino);
-		/* Until the attr cache is working, we may send an additional
-		 * GETATTR */
-		expect_getattr(ino, 0);
-		m_mock->m_child_pid = child;
-		/* Signal the child process to go */
-		ASSERT_EQ(0, sem_post(sem)) << strerror(errno);
-
-		wait(&child_status);
-		ASSERT_EQ(0, WEXITSTATUS(child_status));
-	} else {
-		FAIL() << strerror(errno);
-	}
-	munmap(sem, sizeof(*sem));
+	);
 }
 
 TEST_F(NoAllowOther, disallowed)
 {
-	const char FULLPATH[] = "mountpoint/some_file.txt";
-	int fd;
-	pid_t child;
-	sem_t *sem;
-	int mprot = PROT_READ | PROT_WRITE;
-	int mflags = MAP_ANON | MAP_SHARED;
+	fork(true, [] {
+		}, []() {
+			int fd;
 
-	sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0);
-	ASSERT_NE(MAP_FAILED, sem) << strerror(errno);
-	ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno);
-
-	if ((child = fork()) == 0) {
-		/* In child */
-		int err = 0;
-
-		ASSERT_EQ(0, sem_wait(sem)) << strerror(errno);
-
-		/* Drop privileges before accessing */
-		if (0 != setreuid(-1, m_uid)) {
-			perror("setreuid");
-			err = 1;
-			goto out;
+			fd = open(FULLPATH, O_RDONLY);
+			if (fd >= 0) {
+				fprintf(stderr, "open should've failed\n");
+				return(1);
+			} else if (errno != EPERM) {
+				fprintf(stderr, "Unexpected error: %s\n",
+					strerror(errno));
+				return(1);
+			}
+			return 0;
 		}
-		fd = open(FULLPATH, O_RDONLY);
-		if (fd >= 0) {
-			fprintf(stderr, "open should've failed\n");
-			err = 1;
-		} else if (errno != EPERM) {
-			fprintf(stderr,
-				"Unexpected error: %s\n", strerror(errno));
-			err = 1;
-		}
-
-out:
-		sem_destroy(sem);
-		_exit(0);
-		/* Deliberately leak fd */
-	} else if (child > 0) {
-		/* 
-		 * In parent.  Cleanup must happen here, because it's still
-		 * privileged.
-		 */
-		m_mock->m_child_pid = child;
-		/* Signal the child process to go */
-		ASSERT_EQ(0, sem_post(sem)) << strerror(errno);
-		int child_status;
-
-		wait(&child_status);
-		ASSERT_EQ(0, WEXITSTATUS(child_status));
-	} else {
-		FAIL() << strerror(errno);
-	}
-	munmap(sem, sizeof(*sem));
+	);
 }

Modified: projects/fuse2/tests/sys/fs/fusefs/utils.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/utils.cc	Thu Mar 28 00:25:57 2019	(r345623)
+++ projects/fuse2/tests/sys/fs/fusefs/utils.cc	Thu Mar 28 01:12:44 2019	(r345624)
@@ -28,13 +28,20 @@
  * SUCH DAMAGE.
  */
 
+extern "C" {
 #include <sys/param.h>
+#include <sys/mman.h>
 #include <sys/module.h>
 #include <sys/sysctl.h>
+#include <sys/wait.h>
 
-#include <gtest/gtest.h>
+#include <pwd.h>
+#include <semaphore.h>
 #include <unistd.h>
+}
 
+#include <gtest/gtest.h>
+
 #include "mockfs.hh"
 #include "utils.hh"
 
@@ -239,6 +246,86 @@ void FuseTest::expect_write(uint64_t ino, uint64_t off
 		SET_OUT_HEADER_LEN(out, write);
 		out->body.write.size = osize;
 	})));
+}
+
+static void
+get_unprivileged_uid(uid_t *uid)
+{
+	struct passwd *pw;
+
+	/* 
+	 * First try "tests", Kyua's default unprivileged user.  XXX after
+	 * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API
+	 */
+	pw = getpwnam("tests");
+	if (pw == NULL) {
+		/* Fall back to "nobody" */
+		pw = getpwnam("nobody");
+	}
+	if (pw == NULL)
+		GTEST_SKIP() << "Test requires an unprivileged user";
+	*uid = pw->pw_uid;
+}
+
+void
+FuseTest::fork(bool drop_privs, std::function<void()> parent_func,
+	std::function<int()> child_func)
+{
+	sem_t *sem;
+	int mprot = PROT_READ | PROT_WRITE;
+	int mflags = MAP_ANON | MAP_SHARED;
+	pid_t child;
+	uid_t uid;
+	
+	if (drop_privs) {
+		get_unprivileged_uid(&uid);
+		if (IsSkipped())
+			return;
+	}
+
+	sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0);
+	ASSERT_NE(MAP_FAILED, sem) << strerror(errno);
+	ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno);
+
+	if ((child = ::fork()) == 0) {
+		/* In child */
+		int err = 0;
+
+		if (sem_wait(sem)) {
+			perror("sem_wait");
+			err = 1;
+			goto out;
+		}
+
+		if (drop_privs && 0 != setreuid(-1, uid)) {
+			perror("setreuid");
+			err = 1;
+			goto out;
+		}
+		err = child_func();
+
+out:
+		sem_destroy(sem);
+		_exit(err);
+	} else if (child > 0) {
+		int child_status;
+
+		/* 
+		 * In parent.  Cleanup must happen here, because it's still
+		 * privileged.
+		 */
+		m_mock->m_child_pid = child;
+		ASSERT_NO_FATAL_FAILURE(parent_func());
+
+		/* Signal the child process to go */
+		ASSERT_EQ(0, sem_post(sem)) << strerror(errno);
+
+		wait(&child_status);
+		ASSERT_EQ(0, WEXITSTATUS(child_status));
+	} else {
+		FAIL() << strerror(errno);
+	}
+	munmap(sem, sizeof(*sem));
 }
 
 static void usage(char* progname) {

Modified: projects/fuse2/tests/sys/fs/fusefs/utils.hh
==============================================================================
--- projects/fuse2/tests/sys/fs/fusefs/utils.hh	Thu Mar 28 00:25:57 2019	(r345623)
+++ projects/fuse2/tests/sys/fs/fusefs/utils.hh	Thu Mar 28 01:12:44 2019	(r345624)
@@ -124,4 +124,22 @@ class FuseTest : public ::testing::Test {
 	 */
 	void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
 		uint64_t osize, uint32_t flags, const void *contents);
+
+	/*
+	 * Helper that runs code in a child process.
+	 *
+	 * First, parent_func runs in the parent process.
+	 * Then, child_func runs in the child process, dropping privileges if
+	 * desired.
+	 * Finally, fusetest_fork returns.
+	 *
+	 * # Returns
+	 *
+	 * fusetest_fork will FAIL the test if child_func returns nonzero.
+	 * It may SKIP the test, which the caller should detect with the
+	 * IsSkipped() method.
+	 */
+	void fork(bool drop_privs,
+		std::function<void()> parent_func,
+		std::function<int()> child_func);
 };





Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201903280112.x2S1Ci3S069607>