svn commit: r345624 - projects/fuse2/tests/sys/fs/fusefs
Alan Somers
asomers at FreeBSD.org
Thu Mar 28 01:12:45 UTC 2019
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);
};
More information about the svn-src-projects
mailing list