fusefs: Add tests for the new -o auto_unmount feature
Add tests for mount_fusefs's new -o auto_unmount feature, recently added
by arrowd.
MFC with: 10037d0978 "fusefs: Implement support for the auto_unmount"
This commit is contained in:
@@ -29,6 +29,9 @@
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
@@ -45,6 +48,30 @@ class Destroy: public FuseTest {};
|
||||
/* Tests for unexpected deaths of the server */
|
||||
class Death: public FuseTest{};
|
||||
|
||||
/* Tests for the auto_unmount mount option*/
|
||||
class AutoUnmount: public FuseTest {
|
||||
virtual void SetUp() {
|
||||
m_auto_unmount = true;
|
||||
FuseTest::SetUp();
|
||||
}
|
||||
|
||||
protected:
|
||||
/* Unmounting fusefs might be asynchronous with close, so use a retry loop */
|
||||
void assert_unmounted() {
|
||||
struct statfs statbuf;
|
||||
|
||||
for (int retry = 100; retry > 0; retry--) {
|
||||
ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
|
||||
if (strcmp("fusefs", statbuf.f_fstypename) != 0 &&
|
||||
strcmp("/dev/fuse", statbuf.f_mntfromname) != 0)
|
||||
return;
|
||||
nap();
|
||||
}
|
||||
FAIL() << "fusefs is still mounted";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static void* open_th(void* arg) {
|
||||
int fd;
|
||||
const char *path = (const char*)arg;
|
||||
@@ -55,6 +82,60 @@ static void* open_th(void* arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* With the auto_unmount mount option, the kernel will automatically unmount
|
||||
* the file system when the server dies.
|
||||
*/
|
||||
TEST_F(AutoUnmount, auto_unmount)
|
||||
{
|
||||
/* Kill the daemon */
|
||||
m_mock->kill_daemon();
|
||||
|
||||
/* Use statfs to check that the file system is no longer mounted */
|
||||
assert_unmounted();
|
||||
}
|
||||
|
||||
/*
|
||||
* When -o auto_unmount is used, the kernel should _not_ unmount the file
|
||||
* system when any /dev/fuse file descriptor is closed, but only for the last
|
||||
* one.
|
||||
*/
|
||||
TEST_F(AutoUnmount, dup)
|
||||
{
|
||||
struct statfs statbuf;
|
||||
int fuse2;
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([](auto in) {
|
||||
return (in.header.opcode == FUSE_STATFS);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
|
||||
SET_OUT_HEADER_LEN(out, statfs);
|
||||
})));
|
||||
|
||||
fuse2 = dup_dev_fuse();
|
||||
|
||||
/*
|
||||
* Close one of the /dev/fuse file descriptors. Close the duplicate
|
||||
* first so the daemon thread doesn't freak out when it gets a bunch of
|
||||
* EBADF errors.
|
||||
*/
|
||||
close(fuse2);
|
||||
|
||||
/* Use statfs to check that the file system is still mounted */
|
||||
ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
|
||||
EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
|
||||
EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
|
||||
|
||||
/*
|
||||
* Close the original file descriptor too. Now the file system should be
|
||||
* unmounted at last.
|
||||
*/
|
||||
m_mock->kill_daemon();
|
||||
assert_unmounted();
|
||||
}
|
||||
|
||||
/*
|
||||
* The server dies with unsent operations still on the message queue.
|
||||
* Check for any memory leaks like this:
|
||||
|
||||
@@ -426,7 +426,8 @@ MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
|
||||
bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
|
||||
uint32_t kernel_minor_version, uint32_t max_write, bool async,
|
||||
bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
|
||||
const char *fsname, const char *subtype, bool no_auto_init)
|
||||
const char *fsname, const char *subtype, bool no_auto_init,
|
||||
bool auto_unmount)
|
||||
: m_daemon_id(NULL),
|
||||
m_kernel_minor_version(kernel_minor_version),
|
||||
m_kq(pm == KQ ? kqueue() : -1),
|
||||
@@ -519,6 +520,10 @@ MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
|
||||
build_iovec(&iov, &iovlen, "intr",
|
||||
__DECONST(void*, &trueval), sizeof(bool));
|
||||
}
|
||||
if (auto_unmount) {
|
||||
build_iovec(&iov, &iovlen, "auto_unmount",
|
||||
__DECONST(void*, &trueval), sizeof(bool));
|
||||
}
|
||||
if (*fsname) {
|
||||
build_iovec(&iov, &iovlen, "fsname=",
|
||||
__DECONST(void*, fsname), -1);
|
||||
@@ -787,6 +792,11 @@ void MockFS::init(uint32_t flags) {
|
||||
write(m_fuse_fd, out.get(), out->header.len);
|
||||
}
|
||||
|
||||
int MockFS::dup_dev_fuse()
|
||||
{
|
||||
return (dup(m_fuse_fd));
|
||||
}
|
||||
|
||||
void MockFS::kill_daemon() {
|
||||
m_quit = true;
|
||||
if (m_daemon_id != NULL)
|
||||
|
||||
@@ -372,10 +372,13 @@ class MockFS {
|
||||
uint32_t kernel_minor_version, uint32_t max_write, bool async,
|
||||
bool no_clusterr, unsigned time_gran, bool nointr,
|
||||
bool noatime, const char *fsname, const char *subtype,
|
||||
bool no_auto_init);
|
||||
bool no_auto_init, bool auto_unmount);
|
||||
|
||||
virtual ~MockFS();
|
||||
|
||||
/* Duplicate the /dev/fuse file descriptor, and return the duplicate */
|
||||
int dup_dev_fuse();
|
||||
|
||||
/* Kill the filesystem daemon without unmounting the filesystem */
|
||||
void kill_daemon();
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ void FuseTest::SetUp() {
|
||||
m_pm, m_init_flags, m_kernel_minor_version,
|
||||
m_maxwrite, m_async, m_noclusterr, m_time_gran,
|
||||
m_nointr, m_noatime, m_fsname, m_subtype,
|
||||
m_no_auto_init);
|
||||
m_no_auto_init, m_auto_unmount);
|
||||
/*
|
||||
* FUSE_ACCESS is called almost universally. Expecting it in
|
||||
* each test case would be super-annoying. Instead, set a
|
||||
@@ -571,6 +571,12 @@ get_unprivileged_id(uid_t *uid, gid_t *gid)
|
||||
*gid = gr->gr_gid;
|
||||
}
|
||||
|
||||
int
|
||||
FuseTest::dup_dev_fuse()
|
||||
{
|
||||
return (m_mock->dup_dev_fuse());
|
||||
}
|
||||
|
||||
void
|
||||
FuseTest::fork(bool drop_privs, int *child_status,
|
||||
std::function<void()> parent_func,
|
||||
|
||||
@@ -70,6 +70,7 @@ class FuseTest : public ::testing::Test {
|
||||
bool m_noclusterr;
|
||||
bool m_nointr;
|
||||
bool m_no_auto_init;
|
||||
bool m_auto_unmount;
|
||||
unsigned m_time_gran;
|
||||
MockFS *m_mock = NULL;
|
||||
const static uint64_t FH = 0xdeadbeef1a7ebabe;
|
||||
@@ -97,6 +98,7 @@ class FuseTest : public ::testing::Test {
|
||||
m_noclusterr(false),
|
||||
m_nointr(false),
|
||||
m_no_auto_init(false),
|
||||
m_auto_unmount(false),
|
||||
m_time_gran(1),
|
||||
m_fsname(""),
|
||||
m_subtype(""),
|
||||
@@ -234,6 +236,9 @@ class FuseTest : public ::testing::Test {
|
||||
void expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
|
||||
uint64_t osize, const void *contents);
|
||||
|
||||
/* Duplicate the /dev/fuse file descriptor, and return the duplicate */
|
||||
int dup_dev_fuse();
|
||||
|
||||
/*
|
||||
* Helper that runs code in a child process.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user