Re: [PATCH v1 01/12] VFS: don't count references through ->d_parent
From: Miklos Szeredi
Date: Mon Aug 10 2026 - 07:56:27 EST
On Mon, 3 Aug 2026 at 03:37, NeilBrown <neilb@xxxxxxxxxxx> wrote:
> When a DCACHE_CURSOR dentry is added to d_children, it is only ever
> added "before" or "behind" an existing child, so it will never be the
> first and so never needs to adjust the refcount on the parent. If it
> remains on the list until dput() it could be the last child to be
> removed, in which case normal handling applies in dentry_unlist() (where
> it has been moved from dentry_kill().
Not handling the parent refcount together with cursor removal can
result in inconsistency, e.g:
- add cursor (seek between two positive dentries)
- remove all real children (parent refcount not touched, since cursor
is still on d_children)
- remove cursor (seek to zero offset)
- close directory (dentry_unlist() will skip parent refcount update
since dentry->d_sib is no longer linked)
Repro attached, produces "BUG: Dentry ffff888107ef75d0{i=143e,n=dir}
still in use (1) [unm
ount of hugetlbfs hugetlbfs]".
Thanks,
Milklos
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <err.h>
#include <sys/stat.h>
#define CHECK_ERR(_expr) \
({ typeof(_expr) _ret = (_expr); if (_ret == -1) { err(1, #_expr); } _ret; })
#define CHECK_NULL(_expr) \
({ typeof(_expr) _ret = (_expr); if (_ret == NULL) { errx(1, #_expr " returned NULL"); } _ret; })
int main(void)
{
int dfd;
DIR *dp;
struct dirent *de;
CHECK_ERR(mkdir("dir", 0777));
CHECK_ERR(mknod("dir/foo", 0666, 0));
CHECK_ERR(mknod("dir/bar", 0666, 0));
dfd = CHECK_ERR(open("dir", O_RDONLY));
dp = CHECK_NULL(fdopendir(dfd));
do {
de = CHECK_NULL(readdir(dp));
} while (de->d_name[0] == '.');
CHECK_ERR(lseek(dfd, de->d_off, SEEK_SET)); /* links cursor between foo and bar */
CHECK_ERR(unlink("dir/foo"));
CHECK_ERR(unlink("dir/bar"));
CHECK_ERR(lseek(dfd, 0, SEEK_SET)); /* removes cursor */
CHECK_ERR(closedir(dp));
CHECK_ERR(rmdir("dir"));
}