From nobody Sat Sep 27 14:36:21 2025 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4cYql92T9qz69Pwk; Sat, 27 Sep 2025 14:36:21 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4cYql91k6Fz3xL7; Sat, 27 Sep 2025 14:36:21 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1758983781; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zNN8Kgb+4SxZyl1smJ344za/LJm+/H67b6KCggLNRXA=; b=EeTa1xraCh48LtIe2WbtOqEs66Fv5vCxuA8n3RkIlscgnJ22hWDUawn+JpQ1zJmQoyxE8c F76UFXV3HL6HMyhhTYARQaWiJX1Tic9S9bSTCjAM4dqnczEZMlGOEhEH4g7a0l/PzTNGQD DyHQjGmXeBUgVcejAWtQQF7cUUR/iODyNFWnZr3gnnSx9Nyc18hzwdc/XGNR89Jo98I750 zMGi4dtLRs/96nO8vMcawM0H64yPepfoZcFrUnY7MDFD3K177Ceq4LtwrrAcHb9rXuthIL HZBY3ZpAQHSt+A5ufXc49K0O0VLY7B+h4j8OneZcYTCFr3aMLlTUO3GeEfH3+g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1758983781; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zNN8Kgb+4SxZyl1smJ344za/LJm+/H67b6KCggLNRXA=; b=XzFcwpdoc5UUR0DeNkaXxFmJRHPCbOs2V6m0lFY8obO2c1BVoi0VmT0oY3Dso3YuttkzLL ZycLQlC2P1v1hR+AbwcxZX/vh2HrR8AV5mphBFf3TSqEhW58+mma25OlAY+2b4pydwHd6T 0qs3WPL/wqxz/Lq3ELi0Gw8MjcmrU21usCgXDCH3ztW3bqumfO2chvfJI7J71KpQ720CMr 1Ps7nDFBoNS+Ae+oKv28pKndaFHqUsLKP3/Aqq6PCmnsFEWEK/N9lHoKzyefxtPfKIUixi BcG7JO/b+CIRqznA6TQq75UcAQ8qD9Wl0ZIqM28XalW9Gb6F+LC+RykwyM+ATg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1758983781; a=rsa-sha256; cv=none; b=RJbf27YwJFljM6/nE3w735UMe1XhiRoD/BtQxWjHCHRUknvpw3klu1Rz+IH+g5GAnbrAr9 2dS4ZDVBQpRx9dWjOc/Dw/3xzNkKK/4zYaZP3m15LqOP8GFi6DXGWXi3pVx19xccOHISth va0Iebzg43GeTd4LB8NZfd5dwCAJvGM7wLNkXBWNfopfd0E6rin2oyJC/HcFqWFwBri7DZ fhCZ+NBfkTTzYfXuv6RlPooCVcl2mHDaANvn7o2U89G0hnoVoNOtRV95v3ILomRX8314rx n/zw1nfsgF3LnFrVGqn8DhoN0RAWUP/+mNS6ZgMMULzgjaOf2r7oCEQgCK8Y6g== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4cYql918FCzdTP; Sat, 27 Sep 2025 14:36:21 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 58REaLWO007343; Sat, 27 Sep 2025 14:36:21 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 58REaLmv007340; Sat, 27 Sep 2025 14:36:21 GMT (envelope-from git) Date: Sat, 27 Sep 2025 14:36:21 GMT Message-Id: <202509271436.58REaLmv007340@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Alan Somers Subject: git: a16c01f71fd5 - stable/14 - fusefs: don't fake the mountpoint's stat info before FUSE_INIT completes List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: asomers X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: a16c01f71fd513fec4bc76a572c5b15f3b2369df Auto-Submitted: auto-generated The branch stable/14 has been updated by asomers: URL: https://cgit.FreeBSD.org/src/commit/?id=a16c01f71fd513fec4bc76a572c5b15f3b2369df commit a16c01f71fd513fec4bc76a572c5b15f3b2369df Author: Alan Somers AuthorDate: 2025-06-11 21:26:48 +0000 Commit: Alan Somers CommitDate: 2025-09-27 14:29:36 +0000 fusefs: don't fake the mountpoint's stat info before FUSE_INIT completes Ever since the first GSoC contribution, fusefs has had a curious behavior. If the daemon hasn't finished responding to FUSE_INIT, fuse_vnop_getattr would reply to VOP_GETATTR requests for the mountpoint by returning all zeros. I don't know why. It isn't necessary for unmounting, even if the daemon is dead. Delete that behavior. Now VOP_GETATTR for the mountpoint will wait for the daemon to be ready, just like it will for any other vnode. Reported by: Vassili Tchersky Sponsored by: ConnectWise Differential Revision: https://reviews.freebsd.org/D50800 (cherry picked from commit 9e682c426093a3afc0f609a1f04048b0beb021ec) --- sys/fs/fuse/fuse_vnops.c | 34 ++++++------------- tests/sys/fs/fusefs/pre-init.cc | 72 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 25 deletions(-) diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c index 52e62f121808..265d1604e796 100644 --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -1207,36 +1207,20 @@ fuse_vnop_getattr(struct vop_getattr_args *ap) struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; struct thread *td = curthread; - int err = 0; - int dataflags; - - dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags; - - /* Note that we are not bailing out on a dead file system just yet. */ - if (!(dataflags & FSESS_INITED)) { - if (!vnode_isvroot(vp)) { - fdata_set_dead(fuse_get_mpdata(vnode_mount(vp))); - err = ENOTCONN; - return err; - } else { - goto fake; - } - } err = fuse_internal_getattr(vp, vap, cred, td); if (err == ENOTCONN && vnode_isvroot(vp)) { - /* see comment in fuse_vfsop_statfs() */ - goto fake; - } else { - return err; + /* + * We want to seem a legitimate fs even if the daemon is dead, + * so that, eg., we can still do path based unmounting after + * the daemon dies. + */ + err = 0; + bzero(vap, sizeof(*vap)); + vap->va_type = vnode_vtype(vp); } - -fake: - bzero(vap, sizeof(*vap)); - vap->va_type = vnode_vtype(vp); - - return 0; + return err; } /* diff --git a/tests/sys/fs/fusefs/pre-init.cc b/tests/sys/fs/fusefs/pre-init.cc index e990d3cafffa..2d3257500304 100644 --- a/tests/sys/fs/fusefs/pre-init.cc +++ b/tests/sys/fs/fusefs/pre-init.cc @@ -44,12 +44,26 @@ using namespace testing; /* Tests for behavior that happens before the server responds to FUSE_INIT */ class PreInit: public FuseTest { +public: void SetUp() { m_no_auto_init = true; FuseTest::SetUp(); } }; +/* + * Tests for behavior that happens before the server responds to FUSE_INIT, + * parameterized on default_permissions + */ +class PreInitP: public PreInit, + public WithParamInterface +{ +void SetUp() { + m_default_permissions = GetParam(); + PreInit::SetUp(); +} +}; + static void* unmount1(void* arg __unused) { ssize_t r; @@ -152,3 +166,61 @@ TEST_F(PreInit, signal_during_unmount_before_init) sem_post(&sem0); m_mock->join_daemon(); } + +/* + * If some process attempts VOP_GETATTR for the mountpoint before init is + * complete, fusefs should wait, just like it does for other VOPs. + * + * To verify that fuse_vnop_getattr does indeed wait for FUSE_INIT to complete, + * invoke the test like this: + * +> sudo cpuset -c -l 0 dtrace -i 'fbt:fusefs:fuse_internal_init_callback:' -i 'fbt:fusefs:fuse_vnop_getattr:' -c "./pre-init --gtest_filter=PI/PreInitP.getattr_before_init/0" +... +dtrace: pid 4224 has exited +CPU ID FUNCTION:NAME + 0 68670 fuse_vnop_getattr:entry + 0 68893 fuse_internal_init_callback:entry + 0 68894 fuse_internal_init_callback:return + 0 68671 fuse_vnop_getattr:return + * + * Note that fuse_vnop_getattr was entered first, but exitted last. + */ +TEST_P(PreInitP, getattr_before_init) +{ + struct stat sb; + nlink_t nlink = 12345; + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_INIT); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) { + SET_OUT_HEADER_LEN(out, init); + out.body.init.major = FUSE_KERNEL_VERSION; + out.body.init.minor = FUSE_KERNEL_MINOR_VERSION; + out.body.init.flags = in.body.init.flags & m_init_flags; + out.body.init.max_write = m_maxwrite; + out.body.init.max_readahead = m_maxreadahead; + out.body.init.time_gran = m_time_gran; + nap(); /* Allow stat() to run first */ + }))); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_GETATTR && + in.header.nodeid == FUSE_ROOT_ID); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto& in, auto& out) { + SET_OUT_HEADER_LEN(out, attr); + out.body.attr.attr.ino = in.header.nodeid; + out.body.attr.attr.mode = S_IFDIR | 0644; + out.body.attr.attr.nlink = nlink; + out.body.attr.attr_valid = UINT64_MAX; + }))); + + EXPECT_EQ(0, stat("mountpoint", &sb)); + EXPECT_EQ(nlink, sb.st_nlink); +} + +INSTANTIATE_TEST_SUITE_P(PI, PreInitP, Bool());