Re: [PATCH 1/3] fs/dcache: Add d_clear_dir_neg_dentries()

From: Eric Biggers
Date: Mon Mar 29 2021 - 21:49:40 EST


On Sun, Mar 28, 2021 at 11:43:54AM -0300, André Almeida wrote:
> For directories with negative dentries that are becoming case-insensitive
> dirs, we need to remove all those negative dentries, otherwise they will
> become dangling dentries. During the creation of a new file, if a d_hash
> collision happens and the names match in a case-insensitive way, the name
> of the file will be the name defined at the negative dentry, that may be
> different from the specified by the user. To prevent this from
> happening, we need to remove all dentries in a directory. Given that the
> directory must be empty before we call this function we are sure that
> all dentries there will be negative.
>
> Create a function to remove all negative dentries from a directory, to
> be used as explained above by filesystems that support case-insensitive
> lookups.
>
> Signed-off-by: André Almeida <andrealmeid@xxxxxxxxxxxxx>
> ---
> fs/dcache.c | 27 +++++++++++++++++++++++++++
> include/linux/dcache.h | 1 +
> 2 files changed, 28 insertions(+)
>
> diff --git a/fs/dcache.c b/fs/dcache.c
> index 7d24ff7eb206..fafb3016d6fd 100644
> --- a/fs/dcache.c
> +++ b/fs/dcache.c
> @@ -1723,6 +1723,33 @@ void d_invalidate(struct dentry *dentry)
> }
> EXPORT_SYMBOL(d_invalidate);
>
> +/**
> + * d_clear_dir_neg_dentries - Remove negative dentries in an inode
> + * @dir: Directory to clear negative dentries
> + *
> + * For directories with negative dentries that are becoming case-insensitive
> + * dirs, we need to remove all those negative dentries, otherwise they will
> + * become dangling dentries. During the creation of a new file, if a d_hash
> + * collision happens and the names match in a case-insensitive, the name of
> + * the file will be the name defined at the negative dentry, that can be
> + * different from the specified by the user. To prevent this from happening, we
> + * need to remove all dentries in a directory. Given that the directory must be
> + * empty before we call this function we are sure that all dentries there will
> + * be negative.
> + */
> +void d_clear_dir_neg_dentries(struct inode *dir)
> +{
> + struct dentry *alias, *dentry;
> +
> + hlist_for_each_entry(alias, &dir->i_dentry, d_u.d_alias) {
> + list_for_each_entry(dentry, &alias->d_subdirs, d_child) {
> + d_drop(dentry);
> + dput(dentry);
> + }
> + }
> +}
> +EXPORT_SYMBOL(d_clear_dir_neg_dentries);

As Al already pointed out, this doesn't work as intended, for a number of
different reasons.

Did you consider just using shrink_dcache_parent()? That already does what you
are trying to do here, I think.

The harder part (which I don't think you've considered) is how to ensure that
all negative dentries really get invalidated even if there are lookups of them
happening concurrently. Concurrent lookups can take temporary references to the
negative dentries, preventing them from being invalidated.

- Eric