]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
kernfs: Don't set_nlink for directories being removed
authorT.J. Mercier <tjmercier@google.com>
Wed, 25 Feb 2026 22:34:02 +0000 (14:34 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 Mar 2026 14:51:03 +0000 (15:51 +0100)
If a directory is already in the process of removal its i_nlink count
becomes irrelevant because its contents are also about to be removed and
any pending filesystem operations on it or its contents will soon start
to fail. So we can avoid setting it for directories already flagged for
removal.

This avoids a race in the next patch, which adds clearing of the i_nlink
count for kernfs nodes being removed to support inotify delete events.

Use protection from the kernfs_iattr_rwsem to avoid adding more
contention to the kernfs_rwsem for calls to kernfs_refresh_inode.

Signed-off-by: T.J. Mercier <tjmercier@google.com>
Tested-by: syzbot@syzkaller.appspotmail.com
Link: https://patch.msgid.link/20260225223404.783173-2-tjmercier@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/kernfs/dir.c
fs/kernfs/inode.c

index 8d40c4b1db9fff83e09b0ec9c8e4e47ef5ebf1ad..d9a1707b2148b9d04a412d64f074aee8e12a29b3 100644 (file)
@@ -1491,12 +1491,14 @@ static void __kernfs_remove(struct kernfs_node *kn)
        pr_debug("kernfs %s: removing\n", kernfs_rcu_name(kn));
 
        /* prevent new usage by marking all nodes removing and deactivating */
+       down_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
        pos = NULL;
        while ((pos = kernfs_next_descendant_post(pos, kn))) {
                pos->flags |= KERNFS_REMOVING;
                if (kernfs_active(pos))
                        atomic_add(KN_DEACTIVATED_BIAS, &pos->active);
        }
+       up_write(&kernfs_root(kn)->kernfs_iattr_rwsem);
 
        /* deactivate and unlink the subtree node-by-node */
        do {
index a36aaee98dcec5b8a658ab5bb706b7007c4e62ec..afdc4021e81a5c4bf98acce0008c107808a2b5be 100644 (file)
@@ -178,7 +178,7 @@ static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
                 */
                set_inode_attr(inode, attrs);
 
-       if (kernfs_type(kn) == KERNFS_DIR)
+       if (kernfs_type(kn) == KERNFS_DIR && !(kn->flags & KERNFS_REMOVING))
                set_nlink(inode, kn->dir.subdirs + 2);
 }