--- fs/ext4/namei.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1031,6 +1031,16 @@ errout: return NULL; } +static inline int +is_dot_or_dot_dot(const struct qstr *name) +{ + if (name->name[0] != '.') + return 0; + if (name->len == 1 || (name->len == 2 && name->name[1] == '.')) + return 1; + return 0; +} + static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct inode *inode; @@ -1061,6 +1071,38 @@ static struct dentry *ext4_lookup(struct } } } + + /* ".." shouldn't go into dcache to preserve dcache hierarchy + * otherwise we'll get parent being a child of actual child. + * see bug 10458 for details -bzzz */ + if (inode && is_dot_or_dot_dot(&dentry->d_name)) { + struct dentry *tmp, *goal = NULL; + struct list_head *lp; + + /* first, look for an existing dentry - any one is good */ + spin_lock(&inode->i_lock); + list_for_each(lp, &inode->i_dentry) { + tmp = list_entry(lp, struct dentry, d_alias); + goal = tmp; + dget(goal); + break; + } + if (goal == NULL) { + /* there is no alias, we need to make current dentry: + * a) inaccessible for __d_lookup() + * b) inaccessible for iopen */ + J_ASSERT(list_empty(&dentry->d_alias)); + dentry->d_flags |= DCACHE_NFSFS_RENAMED; + /* this is d_instantiate() ... */ + list_add(&dentry->d_alias, &inode->i_dentry); + dentry->d_inode = inode; + } + spin_unlock(&inode->i_lock); + if (goal) + iput(inode); + return goal; + } + return d_splice_alias(inode, dentry); }