Don't pass inode to ->d_hash() and ->d_compare()
Instances either don't look at it at all (the majority of cases) or only want it to find the superblock (which can be had as dentry->d_sb). A few cases that want more are actually safe with dentry->d_inode - the only precaution needed is the check that it hadn't been replaced with NULL by rmdir() or by overwriting rename(), which case should be simply treated as cache miss. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
27
fs/dcache.c
27
fs/dcache.c
@@ -1723,7 +1723,7 @@ EXPORT_SYMBOL(d_add_ci);
|
||||
* Do the slow-case of the dentry name compare.
|
||||
*
|
||||
* Unlike the dentry_cmp() function, we need to atomically
|
||||
* load the name, length and inode information, so that the
|
||||
* load the name and length information, so that the
|
||||
* filesystem can rely on them, and can use the 'name' and
|
||||
* 'len' information without worrying about walking off the
|
||||
* end of memory etc.
|
||||
@@ -1741,22 +1741,18 @@ enum slow_d_compare {
|
||||
|
||||
static noinline enum slow_d_compare slow_dentry_cmp(
|
||||
const struct dentry *parent,
|
||||
struct inode *inode,
|
||||
struct dentry *dentry,
|
||||
unsigned int seq,
|
||||
const struct qstr *name)
|
||||
{
|
||||
int tlen = dentry->d_name.len;
|
||||
const char *tname = dentry->d_name.name;
|
||||
struct inode *i = dentry->d_inode;
|
||||
|
||||
if (read_seqcount_retry(&dentry->d_seq, seq)) {
|
||||
cpu_relax();
|
||||
return D_COMP_SEQRETRY;
|
||||
}
|
||||
if (parent->d_op->d_compare(parent, inode,
|
||||
dentry, i,
|
||||
tlen, tname, name))
|
||||
if (parent->d_op->d_compare(parent, dentry, tlen, tname, name))
|
||||
return D_COMP_NOMATCH;
|
||||
return D_COMP_OK;
|
||||
}
|
||||
@@ -1766,7 +1762,6 @@ static noinline enum slow_d_compare slow_dentry_cmp(
|
||||
* @parent: parent dentry
|
||||
* @name: qstr of name we wish to find
|
||||
* @seqp: returns d_seq value at the point where the dentry was found
|
||||
* @inode: returns dentry->d_inode when the inode was found valid.
|
||||
* Returns: dentry, or NULL
|
||||
*
|
||||
* __d_lookup_rcu is the dcache lookup function for rcu-walk name
|
||||
@@ -1793,7 +1788,7 @@ static noinline enum slow_d_compare slow_dentry_cmp(
|
||||
*/
|
||||
struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
const struct qstr *name,
|
||||
unsigned *seqp, struct inode *inode)
|
||||
unsigned *seqp)
|
||||
{
|
||||
u64 hashlen = name->hash_len;
|
||||
const unsigned char *str = name->name;
|
||||
@@ -1827,11 +1822,10 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
|
||||
seqretry:
|
||||
/*
|
||||
* The dentry sequence count protects us from concurrent
|
||||
* renames, and thus protects inode, parent and name fields.
|
||||
* renames, and thus protects parent and name fields.
|
||||
*
|
||||
* The caller must perform a seqcount check in order
|
||||
* to do anything useful with the returned dentry,
|
||||
* including using the 'd_inode' pointer.
|
||||
* to do anything useful with the returned dentry.
|
||||
*
|
||||
* NOTE! We do a "raw" seqcount_begin here. That means that
|
||||
* we don't wait for the sequence count to stabilize if it
|
||||
@@ -1845,12 +1839,12 @@ seqretry:
|
||||
continue;
|
||||
if (d_unhashed(dentry))
|
||||
continue;
|
||||
*seqp = seq;
|
||||
|
||||
if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) {
|
||||
if (dentry->d_name.hash != hashlen_hash(hashlen))
|
||||
continue;
|
||||
switch (slow_dentry_cmp(parent, inode, dentry, seq, name)) {
|
||||
*seqp = seq;
|
||||
switch (slow_dentry_cmp(parent, dentry, seq, name)) {
|
||||
case D_COMP_OK:
|
||||
return dentry;
|
||||
case D_COMP_NOMATCH:
|
||||
@@ -1862,6 +1856,7 @@ seqretry:
|
||||
|
||||
if (dentry->d_name.hash_len != hashlen)
|
||||
continue;
|
||||
*seqp = seq;
|
||||
if (!dentry_cmp(dentry, str, hashlen_len(hashlen)))
|
||||
return dentry;
|
||||
}
|
||||
@@ -1959,9 +1954,7 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
|
||||
if (parent->d_flags & DCACHE_OP_COMPARE) {
|
||||
int tlen = dentry->d_name.len;
|
||||
const char *tname = dentry->d_name.name;
|
||||
if (parent->d_op->d_compare(parent, parent->d_inode,
|
||||
dentry, dentry->d_inode,
|
||||
tlen, tname, name))
|
||||
if (parent->d_op->d_compare(parent, dentry, tlen, tname, name))
|
||||
goto next;
|
||||
} else {
|
||||
if (dentry->d_name.len != len)
|
||||
@@ -1998,7 +1991,7 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name)
|
||||
*/
|
||||
name->hash = full_name_hash(name->name, name->len);
|
||||
if (dir->d_flags & DCACHE_OP_HASH) {
|
||||
int err = dir->d_op->d_hash(dir, dir->d_inode, name);
|
||||
int err = dir->d_op->d_hash(dir, name);
|
||||
if (unlikely(err < 0))
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
Reference in New Issue
Block a user