From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Miklos Szeredi To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Al Viro Subject: [PATCH 15/17] vfs: add vfs_get_link() helper Date: Mon, 12 Sep 2016 21:29:17 +0200 Message-Id: <1473708559-12714-16-git-send-email-mszeredi@redhat.com> In-Reply-To: <1473708559-12714-1-git-send-email-mszeredi@redhat.com> References: <1473708559-12714-1-git-send-email-mszeredi@redhat.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: This helper is for filesystems that want to read the symlink and are better off with the get_link() interface (returning a char *) rather than the readlink() interface (copy into a userspace buffer). Also call the LSM hook for readlink (not get_link) since this is for symlink reading not following. Signed-off-by: Miklos Szeredi --- fs/namei.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- include/linux/fs.h | 2 ++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 76d1f061de3c..23c3fb8cec0a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4659,6 +4659,47 @@ out: return len; } +static const char *do_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + const char *link = inode->i_op->get_link(dentry, inode, done); + + /* "jumping" is unacceptable, warn and return error */ + if (!IS_ERR(link) && WARN_ON_ONCE(!link)) + link = ERR_PTR(-EIO); + + return link; +} + +/** + * vfs_get_link - get symbolic link + * @dentry: dentry + * @inode: inode on which to get symbolic link + * @done: caller needs to free returned data with this + * + * Calls security hook and i_op->get_link() on the supplied inode. + * + * It does not touch atime. That's up to the caller if necessary. + * + * Don't call this from RCU lookup mode (yet) + */ +const char *vfs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + const char *res = ERR_PTR(-EINVAL); + + if (inode->i_op->get_link) { + int error = security_inode_readlink(dentry); + + res = ERR_PTR(error); + if (!error) + res = do_get_link(dentry, inode, done); + } + + return res; +} +EXPORT_SYMBOL(vfs_get_link); + /** * vfs_readlink - read symlink body * @dentry: read symlink from this dentry @@ -4682,13 +4723,9 @@ int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) int res; if (!link) { - link = inode->i_op->get_link(dentry, inode, &done); + link = do_get_link(dentry, inode, &done); if (IS_ERR(link)) return PTR_ERR(link); - - /* "jumping" is unacceptable, warn and return error */ - if (WARN_ON_ONCE(!link)) - return -EIO; } res = readlink_copy(buffer, buflen, link); do_delayed_call(&done); diff --git a/include/linux/fs.h b/include/linux/fs.h index f71e70e3017b..58f42e979b12 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2916,6 +2916,8 @@ extern int vfs_fstat(unsigned int, struct kstat *); extern int vfs_fstatat(int , const char __user *, struct kstat *, int); extern int vfs_readlink(struct dentry *, char __user *, int); +extern const char *vfs_get_link(struct dentry *, struct inode *, + struct delayed_call *); extern int __generic_block_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, -- 2.5.5