mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-22 23:13:01 +02:00

->d_revalidate() often needs to access dentry parent and name; that has to be done carefully, since the locking environment varies from caller to caller. We are not guaranteed that dentry in question will not be moved right under us - not unless the filesystem is such that nothing on it ever gets renamed. It can be dealt with, but that results in boilerplate code that isn't even needed - the callers normally have just found the dentry via dcache lookup and want to verify that it's in the right place; they already have the values of ->d_parent and ->d_name stable. There is a couple of exceptions (overlayfs and, to less extent, ecryptfs), but for the majority of calls that song and dance is not needed at all. It's easier to make ecryptfs and overlayfs find and pass those values if there's a ->d_revalidate() instance to be called, rather than doing that in the instances. This commit only changes the calling conventions; making use of supplied values is left to followups. NOTE: some instances need more than just the parent - things like CIFS may need to build an entire path from filesystem root, so they need more precautions than the usual boilerplate. This series doesn't do anything to that need - these filesystems have to keep their locking mechanisms (rename_lock loops, use of dentry_path_raw(), private rwsem a-la v9fs). One thing to keep in mind when using name is that name->name will normally point into the pathname being resolved; the filename in question occupies name->len bytes starting at name->name, and there is NUL somewhere after it, but it the next byte might very well be '/' rather than '\0'. Do not ignore name->len. Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Gabriel Krisman Bertazi <gabriel@krisman.be> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
143 lines
3.6 KiB
C
143 lines
3.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* (C) 2001 Clemson University and The University of Chicago
|
|
*
|
|
* See COPYING in top-level directory.
|
|
*/
|
|
|
|
/*
|
|
* Implementation of dentry (directory cache) functions.
|
|
*/
|
|
|
|
#include "protocol.h"
|
|
#include "orangefs-kernel.h"
|
|
|
|
/* Returns 1 if dentry can still be trusted, else 0. */
|
|
static int orangefs_revalidate_lookup(struct dentry *dentry)
|
|
{
|
|
struct dentry *parent_dentry = dget_parent(dentry);
|
|
struct inode *parent_inode = parent_dentry->d_inode;
|
|
struct orangefs_inode_s *parent = ORANGEFS_I(parent_inode);
|
|
struct inode *inode = dentry->d_inode;
|
|
struct orangefs_kernel_op_s *new_op;
|
|
int ret = 0;
|
|
int err = 0;
|
|
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: attempting lookup.\n", __func__);
|
|
|
|
new_op = op_alloc(ORANGEFS_VFS_OP_LOOKUP);
|
|
if (!new_op) {
|
|
ret = -ENOMEM;
|
|
goto out_put_parent;
|
|
}
|
|
|
|
new_op->upcall.req.lookup.sym_follow = ORANGEFS_LOOKUP_LINK_NO_FOLLOW;
|
|
new_op->upcall.req.lookup.parent_refn = parent->refn;
|
|
strscpy(new_op->upcall.req.lookup.d_name, dentry->d_name.name);
|
|
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG,
|
|
"%s:%s:%d interrupt flag [%d]\n",
|
|
__FILE__,
|
|
__func__,
|
|
__LINE__,
|
|
get_interruptible_flag(parent_inode));
|
|
|
|
err = service_operation(new_op, "orangefs_lookup",
|
|
get_interruptible_flag(parent_inode));
|
|
|
|
/* Positive dentry: reject if error or not the same inode. */
|
|
if (inode) {
|
|
if (err) {
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG,
|
|
"%s:%s:%d lookup failure.\n",
|
|
__FILE__, __func__, __LINE__);
|
|
goto out_drop;
|
|
}
|
|
if (!match_handle(new_op->downcall.resp.lookup.refn.khandle,
|
|
inode)) {
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG,
|
|
"%s:%s:%d no match.\n",
|
|
__FILE__, __func__, __LINE__);
|
|
goto out_drop;
|
|
}
|
|
|
|
/* Negative dentry: reject if success or error other than ENOENT. */
|
|
} else {
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: negative dentry.\n",
|
|
__func__);
|
|
if (!err || err != -ENOENT) {
|
|
if (new_op->downcall.status != 0)
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG,
|
|
"%s:%s:%d lookup failure.\n",
|
|
__FILE__, __func__, __LINE__);
|
|
goto out_drop;
|
|
}
|
|
}
|
|
|
|
orangefs_set_timeout(dentry);
|
|
ret = 1;
|
|
out_release_op:
|
|
op_release(new_op);
|
|
out_put_parent:
|
|
dput(parent_dentry);
|
|
return ret;
|
|
out_drop:
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s:%s:%d revalidate failed\n",
|
|
__FILE__, __func__, __LINE__);
|
|
goto out_release_op;
|
|
}
|
|
|
|
/*
|
|
* Verify that dentry is valid.
|
|
*
|
|
* Should return 1 if dentry can still be trusted, else 0.
|
|
*/
|
|
static int orangefs_d_revalidate(struct inode *dir, const struct qstr *name,
|
|
struct dentry *dentry, unsigned int flags)
|
|
{
|
|
int ret;
|
|
unsigned long time = (unsigned long) dentry->d_fsdata;
|
|
|
|
if (time_before(jiffies, time))
|
|
return 1;
|
|
|
|
if (flags & LOOKUP_RCU)
|
|
return -ECHILD;
|
|
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: called on dentry %p.\n",
|
|
__func__, dentry);
|
|
|
|
/* skip root handle lookups. */
|
|
if (dentry->d_inode && is_root_handle(dentry->d_inode))
|
|
return 1;
|
|
|
|
/*
|
|
* If this passes, the positive dentry still exists or the negative
|
|
* dentry still does not exist.
|
|
*/
|
|
if (!orangefs_revalidate_lookup(dentry))
|
|
return 0;
|
|
|
|
/* We do not need to continue with negative dentries. */
|
|
if (!dentry->d_inode) {
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG,
|
|
"%s: negative dentry or positive dentry and inode valid.\n",
|
|
__func__);
|
|
return 1;
|
|
}
|
|
|
|
/* Now we must perform a getattr to validate the inode contents. */
|
|
|
|
ret = orangefs_inode_check_changed(dentry->d_inode);
|
|
if (ret < 0) {
|
|
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s:%s:%d getattr failure.\n",
|
|
__FILE__, __func__, __LINE__);
|
|
return 0;
|
|
}
|
|
return !ret;
|
|
}
|
|
|
|
const struct dentry_operations orangefs_dentry_operations = {
|
|
.d_revalidate = orangefs_d_revalidate,
|
|
};
|