git: ffb747d587bf - main - fusefs: Add tests for the new -o auto_unmount feature
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 24 Jan 2026 16:04:11 UTC
The branch main has been updated by asomers:
URL: https://cgit.FreeBSD.org/src/commit/?id=ffb747d587bf09a982df67fba322b91d02f70be6
commit ffb747d587bf09a982df67fba322b91d02f70be6
Author: Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2026-01-19 19:11:46 +0000
Commit: Alan Somers <asomers@FreeBSD.org>
CommitDate: 2026-01-24 16:02:33 +0000
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: 10037d0978f "fusefs: Implement support for the auto_unmount"
---
tests/sys/fs/fusefs/destroy.cc | 81 ++++++++++++++++++++++++++++++++++++++++++
tests/sys/fs/fusefs/mockfs.cc | 12 ++++++-
tests/sys/fs/fusefs/mockfs.hh | 5 ++-
tests/sys/fs/fusefs/utils.cc | 8 ++++-
tests/sys/fs/fusefs/utils.hh | 5 +++
5 files changed, 108 insertions(+), 3 deletions(-)
diff --git a/tests/sys/fs/fusefs/destroy.cc b/tests/sys/fs/fusefs/destroy.cc
index 45acb1f99724..0c8e2fd22a50 100644
--- a/tests/sys/fs/fusefs/destroy.cc
+++ b/tests/sys/fs/fusefs/destroy.cc
@@ -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:
diff --git a/tests/sys/fs/fusefs/mockfs.cc b/tests/sys/fs/fusefs/mockfs.cc
index ee47d9e0e01c..b9e4afbbea0b 100644
--- a/tests/sys/fs/fusefs/mockfs.cc
+++ b/tests/sys/fs/fusefs/mockfs.cc
@@ -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)
diff --git a/tests/sys/fs/fusefs/mockfs.hh b/tests/sys/fs/fusefs/mockfs.hh
index 00503332f820..c8f90c2f5402 100644
--- a/tests/sys/fs/fusefs/mockfs.hh
+++ b/tests/sys/fs/fusefs/mockfs.hh
@@ -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();
diff --git a/tests/sys/fs/fusefs/utils.cc b/tests/sys/fs/fusefs/utils.cc
index 125b7e2d6fc7..93b850a7b7e3 100644
--- a/tests/sys/fs/fusefs/utils.cc
+++ b/tests/sys/fs/fusefs/utils.cc
@@ -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,
diff --git a/tests/sys/fs/fusefs/utils.hh b/tests/sys/fs/fusefs/utils.hh
index 91bbba909672..ebd8d41d6961 100644
--- a/tests/sys/fs/fusefs/utils.hh
+++ b/tests/sys/fs/fusefs/utils.hh
@@ -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.
*