When a file is only in a lower layer or in no layer at all, after lookup a negative dentry will be generated in the upper layer or even worse many negetive dentries will be generated in upper/lower layers. These negative dentries will be useless after construction of overlayfs' own dentry and may keep in the memory long time even after unmount of overlayfs instance. This patch tries to kill unnecessary negative dentry during lookup. Signed-off-by: Chengguang Xu <cgxu519@xxxxxxxxxxxx> --- v1->v2: - Only drop negative dentry after slow lookup. fs/namei.c | 9 ++++++--- fs/overlayfs/namei.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/namei.h | 8 ++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a320371899cf..1cc2960c7804 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1386,7 +1386,7 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry, * This looks up the name in dcache and possibly revalidates the found dentry. * NULL is returned if the dentry does not exist in the cache. */ -static struct dentry *lookup_dcache(const struct qstr *name, +struct dentry *lookup_dcache(const struct qstr *name, struct dentry *dir, unsigned int flags) { @@ -1402,6 +1402,7 @@ static struct dentry *lookup_dcache(const struct qstr *name, } return dentry; } +EXPORT_SYMBOL(lookup_dcache); /* * Parent directory has inode locked exclusive. This is one @@ -1500,7 +1501,7 @@ static struct dentry *lookup_fast(struct nameidata *nd, } /* Fast lookup failed, do it the slow way */ -static struct dentry *__lookup_slow(const struct qstr *name, +struct dentry *__lookup_slow(const struct qstr *name, struct dentry *dir, unsigned int flags) { @@ -1536,6 +1537,7 @@ static struct dentry *__lookup_slow(const struct qstr *name, } return dentry; } +EXPORT_SYMBOL(__lookup_slow); static struct dentry *lookup_slow(const struct qstr *name, struct dentry *dir, @@ -2460,7 +2462,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); -static int lookup_one_len_common(const char *name, struct dentry *base, +int lookup_one_len_common(const char *name, struct dentry *base, int len, struct qstr *this) { this->name = name; @@ -2491,6 +2493,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base, return inode_permission(base->d_inode, MAY_EXEC); } +EXPORT_SYMBOL(lookup_one_len_common); /** * try_lookup_one_len - filesystem helper to lookup single pathname component diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 723d17744758..d8e71173ea75 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -191,6 +191,39 @@ static bool ovl_is_opaquedir(struct dentry *dentry) return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE); } +static struct dentry *ovl_lookup_positive_unlocked(const char *name, + struct dentry *base, + int len) +{ + struct qstr this; + struct dentry *ret; + bool not_found = false; + int err; + + err = lookup_one_len_common(name, base, len, &this); + if (err) + return ERR_PTR(err); + + ret = lookup_dcache(&this, base, 0); + if (ret) + return ret; + + inode_lock_shared(base->d_inode); + ret = __lookup_slow(&this, base, 0); + if (!IS_ERR(ret) && + d_flags_negative(ret->d_flags)) { + not_found = true; + d_drop(ret); + } + inode_unlock_shared(base->d_inode); + + if (not_found) { + dput(ret); + ret = ERR_PTR(-ENOENT); + } + return ret; +} + static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, const char *name, unsigned int namelen, size_t prelen, const char *post, @@ -200,7 +233,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, int err; bool last_element = !post[0]; - this = lookup_positive_unlocked(name, base, namelen); + this = ovl_lookup_positive_unlocked(name, base, namelen); if (IS_ERR(this)) { err = PTR_ERR(this); this = NULL; diff --git a/include/linux/namei.h b/include/linux/namei.h index a4bb992623c4..c65b863657eb 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -66,6 +66,14 @@ extern struct dentry *user_path_create(int, const char __user *, struct path *, extern void done_path_create(struct path *, struct dentry *); extern struct dentry *kern_path_locked(const char *, struct path *); +extern int lookup_one_len_common(const char *name, struct dentry *base, + int len, struct qstr *this); +extern struct dentry *lookup_dcache(const struct qstr *name, + struct dentry *base, + unsigned int flags); +extern struct dentry *__lookup_slow(const struct qstr *name, + struct dentry *dir, + unsigned int flags); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); -- 2.20.1