From nobody Sun Apr 26 01:56:23 2026 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 4g38tw29r5z6b8RF for ; Sun, 26 Apr 2026 01:56:24 +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 "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4g38tw01d7z3QK1 for ; Sun, 26 Apr 2026 01:56:24 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1777168584; 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=VRxHrO0OBz5VE0u8w2seCE6eIJN6D3kU/5LE/LdVg78=; b=OD319RgQvCQ5qOsNt9EYe9ywlJ5gDHrHr6qsuBo8BCRyyGdhSkZKFJHsDWrMQZklb7ELnY rIWGsWrPYAe/uZXVFmrQ2Ioe5d8T5NyxW5R8eDUHtvvfTsimId2htRWjjHNIw/z9BMbVE8 4kZF4b6DV6QKjxH435UiOHaKS+YBGeLwZoNDaCbKpNGVpOaAO03XIp6JuMvojFm1jme5G2 4NZGhZv29wQ6US6WZMs0zxy6geFMUXwgW7ypZUk7EtjBd9wlHbcZSD+ij0jC5Bo94oZRSp Y0IHbb2HJQwqghXs32b12cVg051cGodOPTYp77a5c0asyShUCRj7FKHBrzSEsw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1777168584; a=rsa-sha256; cv=none; b=SlLnklePz3QNL9zgmrtZJnzJkV3W8BESYYhrQ7063+4iZsmyuz5re8jnv7m54OmwcEKMjW wj8At6160PKtAgnC+gCslM781d7DGpWvZx+XpBP4TkpHoQ9sIOUKnW2ce8FU6xV6QgsLsk Px0no3iVcDqWokH+H2ctLSaQtctF6ILxNaHU7AK9e23I6I0wie4/5lvBOYMmNPPkblwHVG j7fl3BssztX7ykE4DGALK8FVPmYX4t/Dx80tU4fn5IvSdZZbpnuBU3DbJgZV/GelFda7Pj 31kHleNGYGZFAg2ok0hJ0erZ4Y2B1W0eD8QS4Ezjm1EU585hamkCO/cbBOtBgA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1777168584; 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=VRxHrO0OBz5VE0u8w2seCE6eIJN6D3kU/5LE/LdVg78=; b=rcQaF+K3CUCXTnKetbospBUpvN9aY6ONCskXQCWud8BWAy2Pm2RvcDisxwjxcEIsCRd2Fi /cW/OE8g1pEZo3b8XHfndMlZ5j1nC23srwaMEMCgRuMKQ0JGGrlTXxScwGW6tDca4zIjUV C+Q7X10VVAj6xYUsIhOMuBDQ7uao+wsZFaQ0tJ18QlmXVND5JCOFpln7/fpiNzvMQEDcaU oIhcFChyCaenx4YeDBwn9AAvCvAbQzAUb9MaFK6TqiijvlF0FE5g9XBN/DiL3mbzslEy7B kNSAhNHDaBn1x+mPErLZ+z+NwzhNnoaTbuZZDtSj/+sYMm6oBazkP6zIp1N4fw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4g38tv6LPMz10NN for ; Sun, 26 Apr 2026 01:56:23 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 27bc2 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 26 Apr 2026 01:56:23 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Mark Johnston Subject: git: a02d794f5acd - main - nullfs: Clear inotify flags during reclaim 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: markj X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: a02d794f5acd12ba3cf1de5c204a8dd56af47edd Auto-Submitted: auto-generated Date: Sun, 26 Apr 2026 01:56:23 +0000 Message-Id: <69ed70c7.27bc2.18c1de59@gitrepo.freebsd.org> The branch main has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=a02d794f5acd12ba3cf1de5c204a8dd56af47edd commit a02d794f5acd12ba3cf1de5c204a8dd56af47edd Author: Mark Johnston AuthorDate: 2026-04-26 01:35:37 +0000 Commit: Mark Johnston CommitDate: 2026-04-26 01:56:14 +0000 nullfs: Clear inotify flags during reclaim The inotify flags are copied from the lower vnode into the nullfs vnode so that the INOTIFY() macro will invoke VOP_INOTIFY on the nullfs vnode; this is then bypassed to the lower vnode. However, when a nullfs vnode is reclaimed we should clear these flags, as the vnode is now doomed and no longer forwards VOPs to the lower vnode. Add regression tests. Remove a test in vn_inotify_revoke() which is no longer needed after this change. PR: 292495 Reviewed by: kib Reported by: Jed Laundry Fixes: f1f230439fa4 ("vfs: Initial revision of inotify") MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D56639 --- sys/fs/nullfs/null_vnops.c | 12 +++++ sys/kern/vfs_inotify.c | 4 -- tests/sys/kern/inotify_test.c | 112 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c index a372a46bc6c6..50d359c9b909 100644 --- a/sys/fs/nullfs/null_vnops.c +++ b/sys/fs/nullfs/null_vnops.c @@ -968,6 +968,7 @@ null_reclaim(struct vop_reclaim_args *ap) struct vnode *vp; struct null_node *xp; struct vnode *lowervp; + short flags; vp = ap->a_vp; xp = VTONULL(vp); @@ -997,6 +998,17 @@ null_reclaim(struct vop_reclaim_args *ap) else if (vp->v_writecount < 0) vp->v_writecount = 0; + /* + * Undo the effects of null_copy_inotify(): setting VIRF_INOTIFY* causes + * the VFS to invoke VOP_INOTIFY on the marked vnode, and for nullfs + * vnodes this is bypassed to the lower vnode. The inotify watch holds + * a ref on the lower vnode, but not the upper vnode, so VOP_INOTIFY + * must not be called on the upper vnode after this point. + */ + flags = vn_irflag_read(vp) & (VIRF_INOTIFY | VIRF_INOTIFY_PARENT); + if (flags != 0) + vn_irflag_unset_locked(vp, flags); + VI_UNLOCK(vp); if ((xp->null_flags & NULLV_NOUNLOCK) != 0) diff --git a/sys/kern/vfs_inotify.c b/sys/kern/vfs_inotify.c index 716fdc96e5fb..94e65973a36b 100644 --- a/sys/kern/vfs_inotify.c +++ b/sys/kern/vfs_inotify.c @@ -889,10 +889,6 @@ vn_inotify_add_watch(struct vnode *vp, struct inotify_softc *sc, uint32_t mask, void vn_inotify_revoke(struct vnode *vp) { - if (vp->v_pollinfo == NULL) { - /* This is a nullfs vnode which shadows a watched vnode. */ - return; - } inotify_log(vp, NULL, 0, IN_UNMOUNT, 0); } diff --git a/tests/sys/kern/inotify_test.c b/tests/sys/kern/inotify_test.c index 0a4df4e5fcaa..d3799b12ce20 100644 --- a/tests/sys/kern/inotify_test.c +++ b/tests/sys/kern/inotify_test.c @@ -392,6 +392,116 @@ ATF_TC_CLEANUP(inotify_nullfs, tc) } } +/* + * Watch a file in a nullfs mount, and remove it from the lower mount. Make + * sure that we get an IN_DELETE_SELF event and that the watch is removed. + */ +ATF_TC_WITH_CLEANUP(inotify_nullfs_remove); +ATF_TC_HEAD(inotify_nullfs_remove, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(inotify_nullfs_remove, tc) +{ + char dir[PATH_MAX], path[PATH_MAX], *p; + int error, fd, ifd, wd; + + strlcpy(dir, "./test.XXXXXX", sizeof(dir)); + p = mkdtemp(dir); + ATF_REQUIRE(p == dir); + + error = mkdir("./mnt", 0755); + ATF_REQUIRE(error == 0); + + /* Mount the testdir onto ./mnt. */ + mount_nullfs("./mnt", dir); + + snprintf(path, sizeof(path), "%s/file", dir); + fd = open(path, O_RDWR | O_CREAT, 0644); + ATF_REQUIRE(fd != -1); + close_checked(fd); + + ifd = inotify(IN_NONBLOCK); + wd = inotify_add_watch(ifd, "./mnt/file", IN_DELETE_SELF); + ATF_REQUIRE(wd != -1); + + error = unlink(path); + ATF_REQUIRE(error == 0); + + consume_event(ifd, wd, IN_DELETE_SELF, 0, NULL); + consume_event(ifd, wd, 0, IN_IGNORED, NULL); + + close_inotify(ifd); +} +ATF_TC_CLEANUP(inotify_nullfs_remove, tc) +{ + int error; + + error = unmount("./mnt", 0); + if (error != 0) { + perror("unmount"); + exit(1); + } +} + +/* + * Exercise a scenario where a watched lower vnode is deleted by a rename. The + * deletion causes the upper vnode to be reclaimed, and after that point it + * should stop trying to forward events back to the (now detached) lower vnode. + */ +ATF_TC_WITH_CLEANUP(inotify_nullfs_rename); +ATF_TC_HEAD(inotify_nullfs_rename, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(inotify_nullfs_rename, tc) +{ + char dir[PATH_MAX], path1[PATH_MAX], path2[PATH_MAX], *p; + int error, fd, ifd, wd; + + strlcpy(dir, "./test.XXXXXX", sizeof(dir)); + p = mkdtemp(dir); + ATF_REQUIRE(p == dir); + + error = mkdir("./mnt", 0755); + ATF_REQUIRE(error == 0); + + /* Mount the testdir onto ./mnt. */ + mount_nullfs("./mnt", dir); + + ifd = inotify(IN_NONBLOCK); + + /* Create two files, they will be renamed in the upper layer. */ + snprintf(path1, sizeof(path1), "%s/file1", dir); + fd = open(path1, O_RDWR | O_CREAT, 0644); + ATF_REQUIRE(fd != -1); + close_checked(fd); + snprintf(path2, sizeof(path2), "%s/file2", dir); + fd = open(path2, O_RDWR | O_CREAT, 0644); + ATF_REQUIRE(fd != -1); + close_checked(fd); + + wd = inotify_add_watch(ifd, "./mnt/file1", IN_DELETE_SELF); + ATF_REQUIRE(wd != -1); + error = rename("./mnt/file2", "./mnt/file1"); + ATF_REQUIRE(error == 0); + + consume_event(ifd, wd, IN_DELETE_SELF, 0, NULL); + consume_event(ifd, wd, 0, IN_IGNORED, NULL); + + close_inotify(ifd); +} +ATF_TC_CLEANUP(inotify_nullfs_rename, tc) +{ + int error; + + error = unmount("./mnt", 0); + if (error != 0) { + perror("unmount"); + exit(1); + } +} + /* * Make sure that exceeding max_events pending events results in an overflow * event. @@ -878,6 +988,8 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, inotify_coalesce); ATF_TP_ADD_TC(tp, inotify_mask_create); ATF_TP_ADD_TC(tp, inotify_nullfs); + ATF_TP_ADD_TC(tp, inotify_nullfs_remove); + ATF_TP_ADD_TC(tp, inotify_nullfs_rename); ATF_TP_ADD_TC(tp, inotify_queue_overflow); /* Tests for the various inotify event types. */ ATF_TP_ADD_TC(tp, inotify_event_access_file);