From nobody Tue Aug 5 22:53:28 2025 X-Original-To: dev-commits-src-main@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 4bxTHF0j6Zz63psm; Tue, 05 Aug 2025 22:53:29 +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 "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4bxTHD6zcwz3CXN; Tue, 05 Aug 2025 22:53:28 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1754434409; 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=SenTTyDoLVQPVqdIV06RkM9tZMGyHCRx1sdMVHFazMw=; b=yC2PJmVEWwP+YtECu0D2qSL1WEy0v8Jpl14Z1lBsf55PmWk3loO5D6PvxNX1/T8Wb0qpno Nqo9Bpj3o22HXu3PrzIanlXjfpsU+Twh9Xm951BG3PTmhggRQYp/O7SgoTSZXeQExmGQnz e6I5xwagLCRkJz0eTSdrso8MB7/7/HphBEX3iKSxrGJMdj4cizhKaU0UrcCwiOUX+PdiL1 Ake0+YC/AIWEjObDnWd89VISw/ZtRJJfZBXTREDki0fadoBLqFM6HsbNn7vwvDxZrMfCYk yW+WKxEbSUomICqv31mQGRH08HBVA2nihJxFslUsd71ZWMuolQXzLQOEGvnexg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1754434409; 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=SenTTyDoLVQPVqdIV06RkM9tZMGyHCRx1sdMVHFazMw=; b=tjTFTmukJzW32X4wfbumEebusMYyteM2w6Btf+vvov4/FzIvq2iQeb7zKXLejkI3qPmu6n 56E3BE9l6+mLP+T8FSjendF/R8askIjvfj3n7t1r7QiIJQJDof0GhllyN6b8M5lN6kBAKz JF6jIcDJZrFo6Esk24K/SNA6oJ1VsArsZa2vUfwak32PFSumWhJHpGPF+IEGqxuWEBZ+6h O6+elX/0PlmBmtqQkRift1OE0NEEo4QwSIGuiY/theqlr1Azs1GJXYEYa1cssidcvS9RDM N2lZxXCr1Sq0puw/IcrBXRr6VW5WS/pkrimgDTbDLDUVH3Oy31XIQkTl0EuQQg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1754434409; a=rsa-sha256; cv=none; b=Ic8UlLnQmEo1Oj+e7TOK7Xbd3ahWyTlcZfEUWJt36rJ/qYC481Mjx6LX4gBHJAewTQWS7q PT5Ud73by05ajJG5OcEahn0LyUkIlCcv8AU+2B57GGQKEOkE3LcjLXmXXU63/mbS2OEBbG 4K9yqfBaMk0jiDjcvdZnvVNm8wWwCGRUrq6uK8pgxodKj7UFCdOa1DhF3cifVKWjB0mgkv 3LISiD/+RLvHH8Idj1pRMF5nNZy2hxbvsGcnLs03dbVyFBBo9MZZnBQwMkx7hdvX7+TWhf 3X3J2BUeHqHoz5JaZ1qj+8WQoxrUGmILz3aeXULCNmT9xqG8uLaMGkC0xd2r1Q== 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 4bxTHD6T8vzvZJ; Tue, 05 Aug 2025 22:53:28 +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 575MrSLs069329; Tue, 5 Aug 2025 22:53:28 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 575MrS6U069326; Tue, 5 Aug 2025 22:53:28 GMT (envelope-from git) Date: Tue, 5 Aug 2025 22:53:28 GMT Message-Id: <202508052253.575MrS6U069326@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Alan Somers Subject: git: 9e682c426093 - main - fusefs: don't fake the mountpoint's stat info before FUSE_INIT completes List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@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/main X-Git-Reftype: branch X-Git-Commit: 9e682c426093a3afc0f609a1f04048b0beb021ec Auto-Submitted: auto-generated The branch main has been updated by asomers: URL: https://cgit.FreeBSD.org/src/commit/?id=9e682c426093a3afc0f609a1f04048b0beb021ec commit 9e682c426093a3afc0f609a1f04048b0beb021ec Author: Alan Somers AuthorDate: 2025-06-11 21:26:48 +0000 Commit: Alan Somers CommitDate: 2025-08-05 22:53:05 +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 --- 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 32872e8f3f3a..b90ce60ec664 100644 --- a/sys/fs/fuse/fuse_vnops.c +++ b/sys/fs/fuse/fuse_vnops.c @@ -1219,36 +1219,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))); - return (EXTERROR(ENOTCONN, "FUSE daemon is not " - "initialized")); - } 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());