vfs: export lookup_hash() to modules
Subsystems (2)
Miklos Szeredi
maintainerPATCH 1/2 vfs: export lookup_hash() to modules
Overlayfs needs lookup without inode_permission() and already has the name hash (in form of dentry->d_name on overlayfs dentry). It also doesn't support filesystems with d_op->d_hash() so basically it only needs __lookup_hash() from lookup_one_len().
Rename __lookup_hash() to lookup_hash() and export to modules.
--- fs/namei.c | 35 ++++++++++++++++++++++++++--------- include/linux/namei.h | 2 ++ 2 files changed, 28 insertions(+), 9 deletions(-)
fs/namei.c +26 -9
@@ -1492,8 +1492,24 @@ static struct dentry *lookup_real(structreturn dentry; } -static struct dentry *__lookup_hash (const struct qstr *name, -struct dentry *base,unsigned int flags) +/** +* lookup_hash - lookup single pathname component on already hashed name +* @name: name and hash to lookup +* @base: base directory to lookup from +* @flags: lookup flags +* +* The name must have been verified and hashed (see lookup_one_len()). Using +* this after just full_name_hash() is unsafe. +* +* This function also doesn't check for search permission on base directory. +* +* Use lookup_one_len() or lookup_one_len_unlocked() instead, unless you really +* know what you are doing. +* +* The caller must hold base->i_mutex. +*/ +struct dentry *lookup_hash (const struct qstr *name, +struct dentry *base,unsigned int flags) {struct dentry *dentry =lookup_dcache (name, base, flags);
@@ -1506,6 +1522,7 @@ static struct dentry *__lookup_hash(consreturn lookup_real (base->d_inode, dentry, flags); } +EXPORT_SYMBOL (lookup_hash); static int lookup_fast(struct nameidata *nd, struct path *path, struct inode **inode,
@@ -2229,7 +2246,7 @@ struct dentry *kern_path_locked(const chreturn ERR_PTR (-EINVAL ); }inode_lock_nested (path->dentry->d_inode,I_MUTEX_PARENT ); - d =__lookup_hash (&last, path->dentry,0 ); + d =lookup_hash (&last, path->dentry,0 );if (IS_ERR (d)) {inode_unlock (path->dentry->d_inode);path_put (path);
@@ -2313,7 +2330,7 @@ struct dentry *lookup_one_len(const charif (err)return ERR_PTR (err); -return __lookup_hash (&this, base,0 ); +return lookup_hash (&this, base,0 ); }EXPORT_SYMBOL (lookup_one_len);
@@ -3476,7 +3493,7 @@ static struct dentry *filename_create(in*/ lookup_flags |=LOOKUP_CREATE |LOOKUP_EXCL ;inode_lock_nested (path->dentry->d_inode,I_MUTEX_PARENT ); - dentry =__lookup_hash (&last, path->dentry, lookup_flags); + dentry =lookup_hash (&last, path->dentry, lookup_flags);if (IS_ERR (dentry)) goto unlock;
@@ -3756,7 +3773,7 @@ static long do_rmdir(int dfd, const char goto exit1;inode_lock_nested (path.dentry->d_inode,I_MUTEX_PARENT ); - dentry =__lookup_hash (&last, path.dentry, lookup_flags); + dentry =lookup_hash (&last, path.dentry, lookup_flags); error =PTR_ERR (dentry);if (IS_ERR (dentry)) goto exit2;
@@ -3878,7 +3895,7 @@ static long do_unlinkat(int dfd, const c goto exit1; retry_deleg:inode_lock_nested (path.dentry->d_inode,I_MUTEX_PARENT ); - dentry =__lookup_hash (&last, path.dentry, lookup_flags); + dentry =lookup_hash (&last, path.dentry, lookup_flags); error =PTR_ERR (dentry);if (!IS_ERR (dentry)) {/* Why not before? Because we want correct error value */
@@ -4407,7 +4424,7 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, retry_deleg: trap =lock_rename (new_path.dentry, old_path.dentry); - old_dentry =__lookup_hash (&old_last, old_path.dentry, lookup_flags); + old_dentry =lookup_hash (&old_last, old_path.dentry, lookup_flags); error =PTR_ERR (old_dentry);if (IS_ERR (old_dentry)) goto exit3;
@@ -4415,7 +4432,7 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, error = -ENOENT ;if (d_is_negative (old_dentry)) goto exit4; - new_dentry =__lookup_hash (&new_last, new_path.dentry, lookup_flags | target_flags); + new_dentry =lookup_hash (&new_last, new_path.dentry, lookup_flags | target_flags); error =PTR_ERR (new_dentry);if (IS_ERR (new_dentry)) goto exit4;
include/linux/namei.h +2 -0
@@ -79,6 +79,8 @@ extern int kern_path_mountpoint(int, conextern struct dentry *lookup_one_len (const char *,struct dentry *,int );extern struct dentry *lookup_one_len_unlocked (const char *,struct dentry *,int ); +struct qstr ; +extern struct dentry *lookup_hash (const struct qstr *,struct dentry *,unsigned int );extern int follow_down_one (struct path *);extern int follow_down (struct path *);
Miklos Szeredi
maintainerPATCH 2/2 ovl: ignore permissions on underlying lookup
Generally permission checking is not necessary when overlayfs looks up a dentry on one of the underlying layers, since search permission on base directory was already checked in ovl_permission().
More specifically using lookup_one_len() causes a problem when the lower directory lacks search permission for a specific user while the upper directory does have search permission. Since lookups are cached, this causes inconsistency in behavior: success depends on who did the first lookup.
So instead use lookup_hash() which doesn't do the permission check.
--- fs/overlayfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/overlayfs/super.c +1 -1
@@ -379,7 +379,7 @@ static inline struct dentry *ovl_lookup_struct dentry *dentry;inode_lock (dir->d_inode); - dentry =lookup_one_len (name->name, dir, name->len); + dentry =lookup_hash (name, dir,0 );inode_unlock (dir->d_inode);if (IS_ERR (dentry)) {