From nobody Thu May 07 20:40:25 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 4gBPJn4Ng6z6bwQ7 for ; Thu, 07 May 2026 20:40:25 +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 4gBPJn0zD3z3Nrc for ; Thu, 07 May 2026 20:40:25 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1778186425; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=NnnhsGvuR7+ReRuv6gjufb90+BmSjLvt6luscsPwMss=; b=vS3X2Xm18hE7gwr2ZDZSnB2ViiVYfAymCfGe9ZEUVtCz1amY1FmubNjlB0ByA642qG8sdh zJc+KEAs0YJxkvwn0r6F6b0uaHKphe89+Qv2TfOz3bxdtm2L9qu7Wz46nmUyKuiF3e7/yx FqlUgEpJULJlLHvYK3AoLsbBDkTBhOJmz1/sLj5XzC9bdXNMJ/stWY2TFr+EBsySlmjAyt waXI1rUJMBCBmAQ+XOMmCWbJYJ5Y+8MQhWFJymDFK4/aAC+3loOqgdqqcG2aQXVGOQQ3QU rggnIUPt4nLAugLbFRv+BKH7x6006kmHGK++SCFH1DjUPGPPcA5WKG9cJe1RGw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1778186425; a=rsa-sha256; cv=none; b=fhnr1oml0OD7GQ7Yf+4PNAWyIAbaTT1I5gVOuvm9ulHJ3mC7EZPMpdQpKfxqc1WpDMpw/e zp/PoiHV/s1PniHQKerfBhZG4LdbxBqFxougnoSNlPPmFX3jBYFB8/b65le1bebAOW+6sx 2Duc4+gAvnJtixrnQdONwaYfELrr+3vs7t2sJoxRQj0y0HJKuFMjzTLlcIx+RIkWGGxO1R vLJTYzEFucdqFSbdJ9IB+fAQqTtmZfG7eKJM5FA356sbqDK5FR8QXlFgY10g627PiHhTWi FlQPxP0LzG+jo7hkxf8DXx7l9BFcMpw9Btl/KxZE/ljjWxdm0H5VEJEL0LsREw== 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=1778186425; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=NnnhsGvuR7+ReRuv6gjufb90+BmSjLvt6luscsPwMss=; b=ZQM5+1/p5Ca6LAxBwxD9DOBb57DDw5TkVa9ziB99REbClKU9Z3E20YEtq+X5brq+k7/pEJ pC3iX6X3sD05JmSMKVLT/DLJUiIDIrPkQ39a8ABHoOm1AIXytMXi8OQ85tOnCq2Djax8po E0MohB7YggVlaPZlxZLH8SJWOdB7fOFQfSWvDtjWeDjaPHBGttL4YBKdwAa9rin3eSYkvF XIH1TpJsaZJ2l/yxlLwVy9h7X3+IFJBe8wDHMlJvusYMzxQkOLPpj3MpYbx8G2hc3vdvgc YeFb6R3c2xuUc5xmUGRm1hSy2mVjmg6LOqi8H7VQTnVhsZgyFtiODu20NVjQIw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gBPJn0Lnjzj08 for ; Thu, 07 May 2026 20:40:25 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 22503 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 07 May 2026 20:40:25 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: Mark Johnston From: Colin Percival Subject: git: fdd8f30b9f31 - releng/15.1 - 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: cperciva X-Git-Repository: src X-Git-Refname: refs/heads/releng/15.1 X-Git-Reftype: branch X-Git-Commit: fdd8f30b9f314d04698f864c4b2a80b43a71114a Auto-Submitted: auto-generated Date: Thu, 07 May 2026 20:40:25 +0000 Message-Id: <69fcf8b9.22503.6e76b142@gitrepo.freebsd.org> The branch releng/15.1 has been updated by cperciva: URL: https://cgit.FreeBSD.org/src/commit/?id=fdd8f30b9f314d04698f864c4b2a80b43a71114a commit fdd8f30b9f314d04698f864c4b2a80b43a71114a Author: Mark Johnston AuthorDate: 2026-04-26 01:35:37 +0000 Commit: Colin Percival CommitDate: 2026-05-07 20:40:04 +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. Approved by: re (cperciva) 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) (cherry picked from commit 10567c80b6845717306c5301d104e7440033c4f5) --- 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);