From nobody Sun May 3 15:45:51 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 4g7pym1GZZz6W36X for ; Sun, 03 May 2026 15:45:52 +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 4g7pyl42G1z3JbW for ; Sun, 03 May 2026 15:45:51 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1777823151; 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=wiX8pLzQVWR1W5DL+C5yJnGxdBjoPBT5Zg4dFOH1CDY=; b=i6i7VPGFvSyCtcQNoqA+s6DYz3hNHaZeN4fZF1SJc7yH47/deDAEgpOO8tB+GnZSwFZcOM yYZS54iBoDfccgEVtaeW8fzzJa9H3Dvxi5QCxPXwm5YEGK9vMmWmZKWrh/PhsJsEm0h3bk NeOULRs/nvSh1CtfGVB8j+ogYTPBJl3D9Cy/6DhYXvZ0Ybo+Q2x6PjeYy3Wg0I/CoZo59y H+XiWAi5o0nCXRdRSGeM9ocfEgc0REDxb2aRHjkghIfZbLIsOxQaux+Wp2UTZ/N2o0+igz GYMVag2AYDRJu0Tl94l+gnW0UgUy9clap7/7V1J0JmcgOEiFqM/6+weslEQkyg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1777823151; a=rsa-sha256; cv=none; b=CW7tzDpXOafnUI5fZfCqAxCV7/BN6fD4sZs33LlarP+UCOaGxvh10Qtzgn0atl9vABPW+o gfB8zgcmibSJ2WnSeQF6aGh5XxR0//KvDVOG0rIe1faqVgJTWjUS0pSCVjjSNmzs23/QlX cWjlDg1RTLKCxiyHY71+Nr8ZqDX5EYCm86MnRvQ6xmWPrsvyn03zZElIBu7dQcxvZJlszV jSUK91+vGcXH4srw4OSx4/PxU6mS7npPvoYQZ0nChQjakqtWcVvnYkKV41ItZVqkazNV8F og7yonzlUfl2QSKYw55wnLYyFaNIJX4rZ/QWGDfOPNxAoI7galtvf4rrkd6DUw== 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=1777823151; 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=wiX8pLzQVWR1W5DL+C5yJnGxdBjoPBT5Zg4dFOH1CDY=; b=ywa18JE/Tl+fE4mB2rkoKmZQp894QBxNDBFR8g2JdcxmwexY/rDSlNj5pTZJEWR7NL5/ne IDyCj/nl5LoBA9MTp+lDTdiLeuXzFlxAYv0mv7x/qHB3nqfk6VE2KglHJ16ta1MWNkwLKn HrkUp2QiH62COKw0jm9gwPkTF6PzxIH7BRDBxbJIWe4pxHiC8uSI8sUd9IjBTZqY2awCS1 ClOI8KfSlS0RdxZjSbgNL2fCIADCZzwkJB3zg5yhmTz1Xds9aT5DLHR6wrUVyrepk73hFJ eL2fUhVNywlSjSQjUUxGkxMeMGhO2n93Csq8tM3OD/NJ7xpcpv0YOoyDLHgQ/w== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4g7pyl3HxhzflL for ; Sun, 03 May 2026 15:45:51 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1ebd4 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 03 May 2026 15:45:51 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 10567c80b684 - stable/15 - 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 List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list 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/stable/15 X-Git-Reftype: branch X-Git-Commit: 10567c80b6845717306c5301d104e7440033c4f5 Auto-Submitted: auto-generated Date: Sun, 03 May 2026 15:45:51 +0000 Message-Id: <69f76daf.1ebd4.70b7da66@gitrepo.freebsd.org> The branch stable/15 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=10567c80b6845717306c5301d104e7440033c4f5 commit 10567c80b6845717306c5301d104e7440033c4f5 Author: Mark Johnston AuthorDate: 2026-04-26 01:35:37 +0000 Commit: Mark Johnston CommitDate: 2026-05-03 14:58:57 +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 (cherry picked from commit a02d794f5acd12ba3cf1de5c204a8dd56af47edd) --- 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 a3daa2d8dd54..ee7d89db6b05 100644 --- a/sys/fs/nullfs/null_vnops.c +++ b/sys/fs/nullfs/null_vnops.c @@ -927,6 +927,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); @@ -956,6 +957,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);