}
EXPORT_SYMBOL_GPL(kernfs_put);
-static int kernfs_dop_delete(const struct dentry *dentry)
-{
- struct kernfs_node *kn = dentry->d_fsdata;
- return !(kn && !(kn->flags & KERNFS_REMOVED));
-}
-
static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
{
struct kernfs_node *kn;
if (flags & LOOKUP_RCU)
return -ECHILD;
+ /* Always perform fresh lookup for negatives */
+ if (!dentry->d_inode)
+ goto out_bad_unlocked;
+
kn = dentry->d_fsdata;
mutex_lock(&kernfs_mutex);
out_valid:
return 1;
out_bad:
- /*
- * Remove the dentry from the dcache hashes.
- * If this is a deleted dentry we use d_drop instead of d_delete
- * so kernfs doesn't need to cope with negative dentries.
- *
- * If this is a dentry that has simply been renamed we
- * use d_drop to remove it from the dcache lookup on its
- * old parent. If this dentry persists later when a lookup
- * is performed at its new name the dentry will be readded
- * to the dcache hashes.
- */
mutex_unlock(&kernfs_mutex);
-
- /* If we have submounts we must allow the vfs caches
- * to lie about the state of the filesystem to prevent
- * leaks and other nasty things.
+out_bad_unlocked:
+ /*
+ * @dentry doesn't match the underlying kernfs node, drop the
+ * dentry and force lookup. If we have submounts we must allow the
+ * vfs caches to lie about the state of the filesystem to prevent
+ * leaks and other nasty things, so use check_submounts_and_drop()
+ * instead of d_drop().
*/
if (check_submounts_and_drop(dentry) != 0)
goto out_valid;
const struct dentry_operations kernfs_dops = {
.d_revalidate = kernfs_dop_revalidate,
- .d_delete = kernfs_dop_delete,
.d_release = kernfs_dop_release,
};
struct dentry *dentry,
unsigned int flags)
{
- struct dentry *ret = NULL;
+ struct dentry *ret;
struct kernfs_node *parent = dentry->d_parent->d_fsdata;
struct kernfs_node *kn;
struct inode *inode;
/* no such entry */
if (!kn) {
- ret = ERR_PTR(-ENOENT);
+ ret = NULL;
goto out_unlock;
}
kernfs_get(kn);