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 <mszeredi@xxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html