Commit 19bbb926 authored by Tejun Heo's avatar Tejun Heo Committed by Greg Kroah-Hartman

kernfs: allow negative dentries

kernfs doesn't allow negative dentries - kernfs_iop_lookup() returns
ERR_PTR(-ENOENT) instead of NULL which short-circuits negative dentry
creation and kernfs's d_delete() callback, kernfs_dop_delete(),
returns 1 for all removed nodes.  This in turn allows
kernfs_dop_revalidate() to assume that there's no negative dentry for
kernfs.

This worked fine for sysfs but kernfs is scheduled to grow mkdir(2)
support which depend on negative dentries.  This patch updates so that
kernfs allows negative dentries.  The required changes are almost
trivial - kernfs_iop_lookup() now returns NULL instead of
ERR_PTR(-ENOENT) when the target kernfs_node doesn't exist,
kernfs_dop_delete() is removed and kernfs_dop_revalidate() is updated
to check whether the target dentry is negative and request fresh
lookup if so.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 47a52e91
...@@ -264,12 +264,6 @@ void kernfs_put(struct kernfs_node *kn) ...@@ -264,12 +264,6 @@ void kernfs_put(struct kernfs_node *kn)
} }
EXPORT_SYMBOL_GPL(kernfs_put); 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) static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
{ {
struct kernfs_node *kn; struct kernfs_node *kn;
...@@ -277,6 +271,10 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -277,6 +271,10 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
if (flags & LOOKUP_RCU) if (flags & LOOKUP_RCU)
return -ECHILD; return -ECHILD;
/* Always perform fresh lookup for negatives */
if (!dentry->d_inode)
goto out_bad_unlocked;
kn = dentry->d_fsdata; kn = dentry->d_fsdata;
mutex_lock(&kernfs_mutex); mutex_lock(&kernfs_mutex);
...@@ -301,22 +299,14 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -301,22 +299,14 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
out_valid: out_valid:
return 1; return 1;
out_bad: 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); mutex_unlock(&kernfs_mutex);
out_bad_unlocked:
/* If we have submounts we must allow the vfs caches /*
* to lie about the state of the filesystem to prevent * @dentry doesn't match the underlying kernfs node, drop the
* leaks and other nasty things. * 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) if (check_submounts_and_drop(dentry) != 0)
goto out_valid; goto out_valid;
...@@ -331,7 +321,6 @@ static void kernfs_dop_release(struct dentry *dentry) ...@@ -331,7 +321,6 @@ static void kernfs_dop_release(struct dentry *dentry)
const struct dentry_operations kernfs_dops = { const struct dentry_operations kernfs_dops = {
.d_revalidate = kernfs_dop_revalidate, .d_revalidate = kernfs_dop_revalidate,
.d_delete = kernfs_dop_delete,
.d_release = kernfs_dop_release, .d_release = kernfs_dop_release,
}; };
...@@ -682,7 +671,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, ...@@ -682,7 +671,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
struct dentry *dentry, struct dentry *dentry,
unsigned int flags) unsigned int flags)
{ {
struct dentry *ret = NULL; struct dentry *ret;
struct kernfs_node *parent = dentry->d_parent->d_fsdata; struct kernfs_node *parent = dentry->d_parent->d_fsdata;
struct kernfs_node *kn; struct kernfs_node *kn;
struct inode *inode; struct inode *inode;
...@@ -697,7 +686,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, ...@@ -697,7 +686,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir,
/* no such entry */ /* no such entry */
if (!kn) { if (!kn) {
ret = ERR_PTR(-ENOENT); ret = NULL;
goto out_unlock; goto out_unlock;
} }
kernfs_get(kn); kernfs_get(kn);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment