summary refs log tree commit diff
path: root/fs/namei.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2016-12-09 16:45:04 +0100
committerMiklos Szeredi <mszeredi@redhat.com>2016-12-09 16:45:04 +0100
commit76fca90e9f3abc82114d9d02d8e14e0324a18ca2 (patch)
tree9dc26cb4603b4c93cd6b5b1bb7b538c30d19e438 /fs/namei.c
parentfd4a0edf2a3d781c6ae07d2810776ce22302ee1c (diff)
downloadlinux-76fca90e9f3abc82114d9d02d8e14e0324a18ca2.tar.gz
vfs: default to generic_readlink()
If i_op->readlink is NULL, but i_op->get_link is set then vfs_readlink()
defaults to calling generic_readlink().

The IOP_DEFAULT_READLINK flag indicates that the above conditions are met
and the default action can be taken.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c15
1 files changed, 12 insertions, 3 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 12a4159de72a..b87465d67c60 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4682,10 +4682,19 @@ int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 {
 	struct inode *inode = d_inode(dentry);
 
-	if (!inode->i_op->readlink)
-		return -EINVAL;
+	if (unlikely(!(inode->i_opflags & IOP_DEFAULT_READLINK))) {
+		if (unlikely(inode->i_op->readlink))
+			return inode->i_op->readlink(dentry, buffer, buflen);
+
+		if (!d_is_symlink(dentry))
+			return -EINVAL;
+
+		spin_lock(&inode->i_lock);
+		inode->i_opflags |= IOP_DEFAULT_READLINK;
+		spin_unlock(&inode->i_lock);
+	}
 
-	return inode->i_op->readlink(dentry, buffer, buflen);
+	return generic_readlink(dentry, buffer, buflen);
 }
 EXPORT_SYMBOL(vfs_readlink);