From: T.J. Mercier Date: Wed, 25 Feb 2026 22:34:02 +0000 (-0800) Subject: kernfs: Don't set_nlink for directories being removed X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=507d8ce13f5b91d5b4dca7bd4b4e4249e8021cca;p=thirdparty%2Fkernel%2Flinux.git kernfs: Don't set_nlink for directories being removed 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 Tested-by: syzbot@syzkaller.appspotmail.com Link: https://patch.msgid.link/20260225223404.783173-2-tjmercier@google.com Signed-off-by: Greg Kroah-Hartman --- diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 8d40c4b1db9ff..d9a1707b2148b 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -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 { diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index a36aaee98dcec..afdc4021e81a5 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -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); }