The lookup_one_len(), lookup_one_len_unlocked(), and lookup_positive_unlocked() helpers are used by filesystems targeted in this first iteration to lookup dentries if the caller is privileged over the inode of the base dentry. Add three new helpers lookup_one_len_mapped(), lookup_one_len_mapped_unlocked(), and lookup_one_len_mapped_unlocked() to handle idmapped mounts. If the inode is accessed through an idmapped mount it is mapped according to the mount's user namespace. Afterwards the permissions checks are identical to non-idmapped mounts. If the initial user namespace is passed all mapping operations are a nop so non-idmapped mounts will not see a change in behavior and will also not see any performance impact. It also means that the non-idmapped-mount aware helpers can be implemented on top of their idmapped-mount aware counterparts by passing the initial user namespace. Signed-off-by: Christian Brauner <christian.brauner@xxxxxxxxxx> --- fs/namei.c | 47 ++++++++++++++++++++++++++++++++----------- include/linux/namei.h | 6 ++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a8a3de936cfc..7901ea09e80e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2517,8 +2517,9 @@ 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 len, struct qstr *this) +static int lookup_one_len_common(const char *name, struct dentry *base, int len, + struct qstr *this, + struct user_namespace *mnt_user_ns) { this->name = name; this->len = len; @@ -2546,7 +2547,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base, return err; } - return inode_permission(base->d_inode, MAY_EXEC); + return mapped_inode_permission(mnt_user_ns, base->d_inode, MAY_EXEC); } /** @@ -2570,7 +2571,7 @@ struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, base, len, &this, &init_user_ns); if (err) return ERR_PTR(err); @@ -2589,7 +2590,8 @@ EXPORT_SYMBOL(try_lookup_one_len); * * The caller must hold base->i_mutex. */ -struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +struct dentry *lookup_one_len_mapped(const char *name, struct dentry *base, int len, + struct user_namespace *mnt_user_ns) { struct dentry *dentry; struct qstr this; @@ -2597,13 +2599,19 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, base, len, &this, mnt_user_ns); if (err) return ERR_PTR(err); dentry = lookup_dcache(&this, base, 0); return dentry ? dentry : __lookup_slow(&this, base, 0); } +EXPORT_SYMBOL(lookup_one_len_mapped); + +struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +{ + return lookup_one_len_mapped(name, base, len, &init_user_ns); +} EXPORT_SYMBOL(lookup_one_len); /** @@ -2618,14 +2626,14 @@ EXPORT_SYMBOL(lookup_one_len); * Unlike lookup_one_len, it should be called without the parent * i_mutex held, and will take the i_mutex itself if necessary. */ -struct dentry *lookup_one_len_unlocked(const char *name, - struct dentry *base, int len) +struct dentry *lookup_one_len_mapped_unlocked(const char *name, struct dentry *base, + int len, struct user_namespace *mnt_user_ns) { struct qstr this; int err; struct dentry *ret; - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, base, len, &this, mnt_user_ns); if (err) return ERR_PTR(err); @@ -2634,6 +2642,13 @@ struct dentry *lookup_one_len_unlocked(const char *name, ret = lookup_slow(&this, base, 0); return ret; } +EXPORT_SYMBOL(lookup_one_len_mapped_unlocked); + +struct dentry *lookup_one_len_unlocked(const char *name, + struct dentry *base, int len) +{ + return lookup_one_len_mapped_unlocked(name, base, len, &init_user_ns); +} EXPORT_SYMBOL(lookup_one_len_unlocked); /* @@ -2644,16 +2659,24 @@ EXPORT_SYMBOL(lookup_one_len_unlocked); * need to be very careful; pinned positives have ->d_inode stable, so * this one avoids such problems. */ -struct dentry *lookup_positive_unlocked(const char *name, - struct dentry *base, int len) +struct dentry *lookup_positive_mapped_unlocked(const char *name, + struct dentry *base, int len, + struct user_namespace *mnt_user_ns) { - struct dentry *ret = lookup_one_len_unlocked(name, base, len); + struct dentry *ret = lookup_one_len_mapped_unlocked(name, base, len, mnt_user_ns); if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) { dput(ret); ret = ERR_PTR(-ENOENT); } return ret; } +EXPORT_SYMBOL(lookup_positive_mapped_unlocked); + +struct dentry *lookup_positive_unlocked(const char *name, + struct dentry *base, int len) +{ + return lookup_positive_mapped_unlocked(name, base, len, &init_user_ns); +} EXPORT_SYMBOL(lookup_positive_unlocked); #ifdef CONFIG_UNIX98_PTYS diff --git a/include/linux/namei.h b/include/linux/namei.h index a4bb992623c4..42dbe4c2653a 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -68,8 +68,14 @@ extern struct dentry *kern_path_locked(const char *, struct path *); 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_mapped(const char *, struct dentry *, int, + struct user_namespace *); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); +extern struct dentry *lookup_one_len_mapped_unlocked(const char *, struct dentry *, + int, struct user_namespace *); extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int); +extern struct dentry *lookup_positive_mapped_unlocked(const char *, struct dentry *, + int, struct user_namespace *); extern int follow_down_one(struct path *); extern int follow_down(struct path *); -- 2.29.0