| // 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 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, |
| }; |