From fd4a0edf2a3d781c6ae07d2810776ce22302ee1c Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 9 Dec 2016 16:45:04 +0100 Subject: vfs: replace calling i_op->readlink with vfs_readlink() Also check d_is_symlink() in callers instead of inode->i_op->readlink because following patches will allow NULL ->readlink for symlinks. Signed-off-by: Miklos Szeredi --- include/linux/fs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index dc0478c07b2a..eba20d1c068d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2935,6 +2935,7 @@ extern int vfs_lstat(const char __user *, struct kstat *); extern int vfs_fstat(unsigned int, struct kstat *); extern int vfs_fstatat(int , const char __user *, struct kstat *, int); extern const char *vfs_get_link(struct dentry *, struct delayed_call *); +extern int vfs_readlink(struct dentry *, char __user *, int); extern int __generic_block_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, -- cgit v1.2.3-70-g09d2 From 76fca90e9f3abc82114d9d02d8e14e0324a18ca2 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 9 Dec 2016 16:45:04 +0100 Subject: 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 --- Documentation/filesystems/porting | 4 ++++ Documentation/filesystems/vfs.txt | 9 ++++++--- fs/namei.c | 15 ++++++++++++--- include/linux/fs.h | 1 + 4 files changed, 23 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index bdd025ceb763..95280079c0b3 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -596,3 +596,7 @@ in your dentry operations instead. [mandatory] ->rename() has an added flags argument. Any flags not handled by the filesystem should result in EINVAL being returned. +-- +[recommended] + ->readlink is optional for symlinks. Don't set, unless filesystem needs + to fake something for readlink(2). diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index b5039a00caaf..038241123ca5 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -451,9 +451,6 @@ otherwise noted. exist; this is checked by the VFS. Unlike plain rename, source and target may be of different type. - readlink: called by the readlink(2) system call. Only required if - you want to support reading symbolic links - get_link: called by the VFS to follow a symbolic link to the inode it points to. Only required if you want to support symbolic links. This method returns the symlink body @@ -468,6 +465,12 @@ otherwise noted. argument. If request can't be handled without leaving RCU mode, have it return ERR_PTR(-ECHILD). + readlink: this is now just an override for use by readlink(2) for the + cases when ->get_link uses nd_jump_link() or object is not in + fact a symlink. Normally filesystems should only implement + ->get_link for symlinks and readlink(2) will automatically use + that. + permission: called by the VFS to check for access rights on a POSIX-like filesystem. 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); diff --git a/include/linux/fs.h b/include/linux/fs.h index eba20d1c068d..f6c206eae6ac 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -595,6 +595,7 @@ is_uncached_acl(struct posix_acl *acl) #define IOP_LOOKUP 0x0002 #define IOP_NOFOLLOW 0x0004 #define IOP_XATTR 0x0008 +#define IOP_DEFAULT_READLINK 0x0010 /* * Keep mostly read-only and often accessed (especially for -- cgit v1.2.3-70-g09d2 From d16744ec8ad011793144bb932ce822cc0c1733cb Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 9 Dec 2016 16:45:04 +0100 Subject: vfs: make generic_readlink() static Signed-off-by: Miklos Szeredi --- fs/namei.c | 4 ++-- include/linux/fs.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index c9baf3b3ffd7..c248a9e1edd2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4650,7 +4650,8 @@ out: * have ->get_link() not calling nd_jump_link(). Using (or not using) it * for any given inode is up to filesystem. */ -int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) +static int generic_readlink(struct dentry *dentry, char __user *buffer, + int buflen) { DEFINE_DELAYED_CALL(done); struct inode *inode = d_inode(dentry); @@ -4666,7 +4667,6 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) do_delayed_call(&done); return res; } -EXPORT_SYMBOL(generic_readlink); /** * vfs_readlink - copy symlink body into userspace buffer diff --git a/include/linux/fs.h b/include/linux/fs.h index f6c206eae6ac..e343d784651a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2915,7 +2915,6 @@ extern int __page_symlink(struct inode *inode, const char *symname, int len, extern int page_symlink(struct inode *inode, const char *symname, int len); extern const struct inode_operations page_symlink_inode_operations; extern void kfree_link(void *); -extern int generic_readlink(struct dentry *, char __user *, int); extern void generic_fillattr(struct inode *, struct kstat *); int vfs_getattr_nosec(struct path *path, struct kstat *stat); extern int vfs_getattr(struct path *, struct kstat *); -- cgit v1.2.3-70-g09d2