fuse(4): add tests for some mount options.
This commit adds tests for the default_permissions and push_symlinks_in mount options. It doesn't add tests for allow_other, because I'm not sure how that will interact with Kyua (the test will need to drop privileges). All of the other mount options are undocumented. PR: 216391 Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
1f9aeb4e2f
commit
be65471ae9
@ -9,6 +9,7 @@ TESTSDIR= ${TESTSBASE}/sys/fs/fuse
|
||||
# out, so we get more granular reporting.
|
||||
ATF_TESTS_CXX+= access
|
||||
ATF_TESTS_CXX+= create
|
||||
ATF_TESTS_CXX+= default_permissions
|
||||
ATF_TESTS_CXX+= destroy
|
||||
ATF_TESTS_CXX+= flush
|
||||
ATF_TESTS_CXX+= fsync
|
||||
@ -46,6 +47,11 @@ SRCS.create+= getmntopts.c
|
||||
SRCS.create+= mockfs.cc
|
||||
SRCS.create+= utils.cc
|
||||
|
||||
SRCS.default_permissions+= default_permissions.cc
|
||||
SRCS.default_permissions+= getmntopts.c
|
||||
SRCS.default_permissions+= mockfs.cc
|
||||
SRCS.default_permissions+= utils.cc
|
||||
|
||||
SRCS.destroy+= destroy.cc
|
||||
SRCS.destroy+= getmntopts.c
|
||||
SRCS.destroy+= mockfs.cc
|
||||
|
@ -29,7 +29,6 @@
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
//#include <sys/param.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
@ -59,8 +58,6 @@ void expect_lookup(const char *relpath, uint64_t ino)
|
||||
}
|
||||
};
|
||||
|
||||
/* TODO: test methods for the default_permissions mount option */
|
||||
|
||||
/* The error case of FUSE_ACCESS. */
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236291 */
|
||||
TEST_F(Access, DISABLED_eaccess)
|
||||
|
136
tests/sys/fs/fuse/default_permissions.cc
Normal file
136
tests/sys/fs/fuse/default_permissions.cc
Normal file
@ -0,0 +1,136 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 The FreeBSD Foundation
|
||||
*
|
||||
* This software was developed by BFF Storage Systems, LLC under sponsorship
|
||||
* from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tests for the "default_permissions" mount option. They must be in their own
|
||||
* file so they can be run as an unprivileged user
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
#include "mockfs.hh"
|
||||
#include "utils.hh"
|
||||
|
||||
using namespace testing;
|
||||
|
||||
class DefaultPermissions: public FuseTest {
|
||||
|
||||
virtual void SetUp() {
|
||||
if (geteuid() == 0) {
|
||||
FAIL() << "This test requires an unprivileged user";
|
||||
}
|
||||
m_default_permissions = true;
|
||||
FuseTest::SetUp();
|
||||
}
|
||||
|
||||
public:
|
||||
void expect_lookup(const char *relpath, uint64_t ino, mode_t mode)
|
||||
{
|
||||
FuseTest::expect_lookup(relpath, ino, S_IFREG | mode, 0, 1);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class Access: public DefaultPermissions {};
|
||||
class Open: public DefaultPermissions {};
|
||||
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */
|
||||
TEST_F(Access, DISABLED_eaccess)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
uint64_t ino = 42;
|
||||
mode_t access_mode = X_OK;
|
||||
|
||||
expect_lookup(RELPATH, ino, S_IFREG | 0644);
|
||||
/*
|
||||
* Once default_permissions is properly implemented, there might be
|
||||
* another FUSE_GETATTR or something in here. But there should not be
|
||||
* a FUSE_ACCESS
|
||||
*/
|
||||
|
||||
ASSERT_NE(0, access(FULLPATH, access_mode));
|
||||
ASSERT_EQ(EACCES, errno);
|
||||
}
|
||||
|
||||
TEST_F(Access, ok)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
uint64_t ino = 42;
|
||||
mode_t access_mode = R_OK;
|
||||
|
||||
expect_lookup(RELPATH, ino, S_IFREG | 0644);
|
||||
/*
|
||||
* Once default_permissions is properly implemented, there might be
|
||||
* another FUSE_GETATTR or something in here. But there should not be
|
||||
* a FUSE_ACCESS
|
||||
*/
|
||||
|
||||
ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
|
||||
}
|
||||
|
||||
TEST_F(Open, ok)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
|
||||
expect_lookup(RELPATH, ino, S_IFREG | 0644);
|
||||
expect_open(ino, 0, 1);
|
||||
/* Until the attr cache is working, we may send an additional GETATTR */
|
||||
expect_getattr(ino, 0);
|
||||
|
||||
fd = open(FULLPATH, O_RDONLY);
|
||||
EXPECT_LE(0, fd) << strerror(errno);
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
||||
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */
|
||||
TEST_F(Open, DISABLED_eperm)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
uint64_t ino = 42;
|
||||
|
||||
expect_lookup(RELPATH, ino, S_IFREG | 0644);
|
||||
/*
|
||||
* Once default_permissions is properly implemented, there might be
|
||||
* another FUSE_GETATTR or something in here. But there should not be
|
||||
* a FUSE_ACCESS
|
||||
*/
|
||||
|
||||
EXPECT_NE(0, open(FULLPATH, O_RDWR));
|
||||
EXPECT_EQ(EPERM, errno);
|
||||
}
|
@ -243,7 +243,9 @@ void debug_fuseop(const mockfs_buf_in *in)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
MockFS::MockFS(int max_readahead, uint32_t flags) {
|
||||
MockFS::MockFS(int max_readahead, bool push_symlinks_in,
|
||||
bool default_permissions, uint32_t flags)
|
||||
{
|
||||
struct iovec *iov = NULL;
|
||||
int iovlen = 0;
|
||||
char fdstr[15];
|
||||
@ -277,6 +279,16 @@ MockFS::MockFS(int max_readahead, uint32_t flags) {
|
||||
__DECONST(void *, "mountpoint"), -1);
|
||||
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
|
||||
build_iovec(&iov, &iovlen, "fd", fdstr, -1);
|
||||
if (push_symlinks_in) {
|
||||
const bool trueval = true;
|
||||
build_iovec(&iov, &iovlen, "push_symlinks_in",
|
||||
__DECONST(void*, &trueval), sizeof(bool));
|
||||
}
|
||||
if (default_permissions) {
|
||||
const bool trueval = true;
|
||||
build_iovec(&iov, &iovlen, "default_permissions",
|
||||
__DECONST(void*, &trueval), sizeof(bool));
|
||||
}
|
||||
if (nmount(iov, iovlen, 0))
|
||||
throw(std::system_error(errno, std::system_category(),
|
||||
"Couldn't mount filesystem"));
|
||||
|
@ -212,7 +212,8 @@ class MockFS {
|
||||
uint32_t m_max_write;
|
||||
|
||||
/* Create a new mockfs and mount it to a tempdir */
|
||||
MockFS(int max_readahead, uint32_t flags);
|
||||
MockFS(int max_readahead, bool push_symlinks_in,
|
||||
bool default_permissions, uint32_t flags);
|
||||
virtual ~MockFS();
|
||||
|
||||
/* Kill the filesystem daemon without unmounting the filesystem */
|
||||
|
@ -29,6 +29,8 @@
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
@ -43,6 +45,24 @@ void expect_lookup(const char *relpath, uint64_t ino)
|
||||
{
|
||||
FuseTest::expect_lookup(relpath, ino, S_IFLNK | 0777, 0, 1);
|
||||
}
|
||||
void expect_readlink(uint64_t ino, ProcessMockerT r)
|
||||
{
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_READLINK &&
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(r));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class PushSymlinksIn: public Readlink {
|
||||
virtual void SetUp() {
|
||||
m_push_symlinks_in = true;
|
||||
Readlink::SetUp();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(Readlink, eloop)
|
||||
@ -53,14 +73,7 @@ TEST_F(Readlink, eloop)
|
||||
char buf[80];
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_READLINK &&
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnErrno(ELOOP)));
|
||||
expect_readlink(ino, ReturnErrno(ELOOP));
|
||||
|
||||
EXPECT_EQ(-1, readlink(FULLPATH, buf, sizeof(buf)));
|
||||
EXPECT_EQ(ELOOP, errno);
|
||||
@ -75,20 +88,35 @@ TEST_F(Readlink, ok)
|
||||
char buf[80];
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_READLINK &&
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
|
||||
expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto out) {
|
||||
strlcpy(out->body.str, dst, sizeof(out->body.str));
|
||||
out->header.len = sizeof(out->header) + strlen(dst) + 1;
|
||||
})));
|
||||
|
||||
}));
|
||||
|
||||
EXPECT_EQ((ssize_t)strlen(dst) + 1,
|
||||
readlink(FULLPATH, buf, sizeof(buf)));
|
||||
EXPECT_STREQ(dst, buf);
|
||||
}
|
||||
|
||||
TEST_F(PushSymlinksIn, readlink)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/src";
|
||||
const char RELPATH[] = "src";
|
||||
const char dst[] = "/dst";
|
||||
const uint64_t ino = 42;
|
||||
char buf[MAXPATHLEN], wd[MAXPATHLEN], want[MAXPATHLEN];
|
||||
int len;
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto out) {
|
||||
strlcpy(out->body.str, dst, sizeof(out->body.str));
|
||||
out->header.len = sizeof(out->header) + strlen(dst) + 1;
|
||||
}));
|
||||
|
||||
ASSERT_NE(NULL, getcwd(wd, sizeof(wd))) << strerror(errno);
|
||||
len = snprintf(want, sizeof(want), "%s/mountpoint%s", wd, dst);
|
||||
ASSERT_LE(0, len) << strerror(errno);
|
||||
|
||||
EXPECT_EQ((ssize_t)len + 1, readlink(FULLPATH, buf, sizeof(buf)));
|
||||
EXPECT_STREQ(want, buf);
|
||||
}
|
||||
|
@ -78,7 +78,8 @@ void FuseTest::SetUp() {
|
||||
m_maxbcachebuf = val;
|
||||
|
||||
try {
|
||||
m_mock = new MockFS(m_maxreadahead, m_init_flags);
|
||||
m_mock = new MockFS(m_maxreadahead, m_push_symlinks_in,
|
||||
m_default_permissions, m_init_flags);
|
||||
} catch (std::system_error err) {
|
||||
FAIL() << err.what();
|
||||
}
|
||||
|
@ -41,20 +41,24 @@ class FuseTest : public ::testing::Test {
|
||||
protected:
|
||||
uint32_t m_maxreadahead;
|
||||
uint32_t m_init_flags;
|
||||
bool m_default_permissions;
|
||||
bool m_push_symlinks_in;
|
||||
MockFS *m_mock = NULL;
|
||||
const static uint64_t FH = 0xdeadbeef1a7ebabe;
|
||||
|
||||
public:
|
||||
int m_maxbcachebuf;
|
||||
|
||||
FuseTest():
|
||||
/*
|
||||
* libfuse's default max_readahead is UINT_MAX, though it can be
|
||||
* lowered
|
||||
* libfuse's default max_readahead is UINT_MAX, though it can
|
||||
* be lowered
|
||||
*/
|
||||
FuseTest(): FuseTest(UINT_MAX, 0) {}
|
||||
FuseTest(uint32_t maxreadahead): m_maxreadahead(maxreadahead) {}
|
||||
FuseTest(uint32_t maxreadahead, uint32_t init_flags):
|
||||
m_maxreadahead(maxreadahead), m_init_flags(init_flags) {}
|
||||
m_maxreadahead(UINT_MAX),
|
||||
m_init_flags(0),
|
||||
m_default_permissions(false),
|
||||
m_push_symlinks_in(false)
|
||||
{}
|
||||
|
||||
virtual void SetUp();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user