git: 95d7a7d17804 - stable/14 - inotify: Unconditionally generate IN_IGNORED events for files/dirs

Mark Johnston <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a7353e2.451e9.7bdde67c__30807.9957308777$1785943291$gmane$org@gitrepo.freebsd.org>
The branch stable/14 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=95d7a7d17804738f9d7caf848c159792b3bde4c0

commit 95d7a7d17804738f9d7caf848c159792b3bde4c0
Author:     Mark Johnston <[email protected]>
AuthorDate: 2026-07-06 12:50:37 +0000
Commit:     Mark Johnston <[email protected]>
CommitDate: 2026-08-05 15:15:16 +0000

    inotify: Unconditionally generate IN_IGNORED events for files/dirs
    
    The implementation previously only generated an IN_IGNORED event for a
    deleted watched file if the watch explicitly requested IN_DELETE_SELF.
    This is not correct, IN_IGNORED should always be raised when the watched
    subject is deleted.  Adjust the implementation of inotify_log_one()
    accordingly.
    
    This also fixes a problem where a deleted watched file's watch
    would not be removed if IN_DELETE_SELF was not in the watch's event
    mask, in which case the unlinked vnode would linger until the inotify
    descriptor itself is closed.
    
    Add a regression test.
    
    Reported by:    jrtc27
    Reviewed by:    jrtc27
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D58050
    
    (cherry picked from commit b70997c8c75adc3ab343c47d5ba7d01c9c774d9e)
---
 sys/kern/vfs_inotify.c        | 35 +++++++++++++++++++++++------------
 tests/sys/kern/inotify_test.c | 24 ++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/sys/kern/vfs_inotify.c b/sys/kern/vfs_inotify.c
index e003759348a3..b44989bf5583 100644
--- a/sys/kern/vfs_inotify.c
+++ b/sys/kern/vfs_inotify.c
@@ -610,25 +610,37 @@ inotify_log_one(struct inotify_watch *watch, const char *name, size_t namelen,
 	struct inotify_watch key;
 	struct inotify_softc *sc;
 	struct inotify_record *rec;
-	bool allocfail;
+	bool allocfail, delete, notify, oneshot, unmount;
 
 	mtx_assert(&watch->vp->v_pollinfo->vpi_lock, MA_OWNED);
 
 	sc = watch->sc;
-	rec = inotify_alloc_record(watch->wd, name, namelen, event, cookie,
-	    M_NOWAIT);
-	if (rec == NULL) {
-		rec = &sc->overflow;
-		allocfail = true;
+
+	delete = (event & IN_DELETE_SELF) != 0;
+	notify = (watch->mask & event) != 0;
+	oneshot = (watch->mask & IN_ONESHOT) != 0;
+	unmount = (event & IN_UNMOUNT) != 0;
+	if (!notify && !delete && !unmount)
+		return;
+
+	if (notify || unmount) {
+		rec = inotify_alloc_record(watch->wd, name, namelen, event,
+		    cookie, M_NOWAIT);
+		if (rec == NULL) {
+			rec = &sc->overflow;
+			allocfail = true;
+		} else {
+			allocfail = false;
+		}
 	} else {
-		allocfail = false;
+		rec = NULL;
 	}
 
 	mtx_lock(&sc->lock);
-	if (!inotify_queue_record(sc, rec) && rec != &sc->overflow)
+	if (rec != NULL && !inotify_queue_record(sc, rec) &&
+	    rec != &sc->overflow)
 		free(rec, M_INOTIFY);
-	if ((watch->mask & IN_ONESHOT) != 0 ||
-	    (event & (IN_DELETE_SELF | IN_UNMOUNT)) != 0) {
+	if (oneshot || delete || unmount) {
 		if (!allocfail) {
 			rec = inotify_alloc_record(watch->wd, NULL, 0,
 			    IN_IGNORED, 0, M_NOWAIT);
@@ -672,8 +684,7 @@ inotify_log(struct vnode *vp, const char *name, size_t namelen, int event,
 	TAILQ_FOREACH_SAFE(watch, &vp->v_pollinfo->vpi_inotify, vlink, tmp) {
 		KASSERT(watch->vp == vp,
 		    ("inotify_log: watch %p vp != vp", watch));
-		if ((watch->mask & event) != 0 || event == IN_UNMOUNT)
-			inotify_log_one(watch, name, namelen, event, cookie);
+		inotify_log_one(watch, name, namelen, event, cookie);
 	}
 	mtx_unlock(&vp->v_pollinfo->vpi_lock);
 }
diff --git a/tests/sys/kern/inotify_test.c b/tests/sys/kern/inotify_test.c
index b4fa21862063..bf0af87f0a0c 100644
--- a/tests/sys/kern/inotify_test.c
+++ b/tests/sys/kern/inotify_test.c
@@ -53,6 +53,8 @@ ev2name(int event)
 		return ("IN_MOVED_TO");
 	case IN_OPEN:
 		return ("IN_OPEN");
+	case IN_IGNORED:
+		return ("IN_IGNORED");
 	default:
 		return (NULL);
 	}
@@ -869,6 +871,27 @@ ATF_TC_BODY(inotify_event_delete, tc)
 	close_inotify(ifd);
 }
 
+ATF_TC_WITHOUT_HEAD(inotify_event_delete_ignored);
+ATF_TC_BODY(inotify_event_delete_ignored, tc)
+{
+	char path[PATH_MAX];
+	int error, ifd, wd;
+
+	ifd = inotify(IN_NONBLOCK);
+
+	wd = watch_dir(ifd, IN_DELETE, path);
+
+	/*
+	 * IN_IGNORED should be generated when a watched directory is removed,
+	 * even if the watch was not configured to raise IN_DELETE_SELF.
+	 */
+	error = rmdir(path);
+	ATF_REQUIRE(error == 0);
+	consume_event(ifd, wd, 0, IN_IGNORED, NULL);
+
+	close_inotify(ifd);
+}
+
 ATF_TC_WITHOUT_HEAD(inotify_event_move);
 ATF_TC_BODY(inotify_event_move, tc)
 {
@@ -1036,6 +1059,7 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, inotify_event_close_write);
 	ATF_TP_ADD_TC(tp, inotify_event_create);
 	ATF_TP_ADD_TC(tp, inotify_event_delete);
+	ATF_TP_ADD_TC(tp, inotify_event_delete_ignored);
 	ATF_TP_ADD_TC(tp, inotify_event_move);
 	ATF_TP_ADD_TC(tp, inotify_event_move_dir);
 	ATF_TP_ADD_TC(tp, inotify_event_open);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.