Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <mszeredi@redhat.com>

+35 -13
+21
fs/namei.c
··· 4669 4669 EXPORT_SYMBOL(generic_readlink); 4670 4670 4671 4671 /** 4672 + * vfs_readlink - copy symlink body into userspace buffer 4673 + * @dentry: dentry on which to get symbolic link 4674 + * @buffer: user memory pointer 4675 + * @buflen: size of buffer 4676 + * 4677 + * Does not touch atime. That's up to the caller if necessary 4678 + * 4679 + * Does not call security hook. 4680 + */ 4681 + int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) 4682 + { 4683 + struct inode *inode = d_inode(dentry); 4684 + 4685 + if (!inode->i_op->readlink) 4686 + return -EINVAL; 4687 + 4688 + return inode->i_op->readlink(dentry, buffer, buflen); 4689 + } 4690 + EXPORT_SYMBOL(vfs_readlink); 4691 + 4692 + /** 4672 4693 * vfs_get_link - get symlink body 4673 4694 * @dentry: dentry on which to get symbolic link 4674 4695 * @done: caller needs to free returned data with this
+4 -4
fs/nfsd/nfs4xdr.c
··· 3576 3576 if (!p) 3577 3577 return nfserr_resource; 3578 3578 /* 3579 - * XXX: By default, the ->readlink() VFS op will truncate symlinks 3580 - * if they would overflow the buffer. Is this kosher in NFSv4? If 3581 - * not, one easy fix is: if ->readlink() precisely fills the buffer, 3582 - * assume that truncation occurred, and return NFS4ERR_RESOURCE. 3579 + * XXX: By default, vfs_readlink() will truncate symlinks if they 3580 + * would overflow the buffer. Is this kosher in NFSv4? If not, one 3581 + * easy fix is: if vfs_readlink() precisely fills the buffer, assume 3582 + * that truncation occurred, and return NFS4ERR_RESOURCE. 3583 3583 */ 3584 3584 nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, 3585 3585 (char *)p, &maxcount);
+2 -4
fs/nfsd/vfs.c
··· 1451 1451 __be32 1452 1452 nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) 1453 1453 { 1454 - struct inode *inode; 1455 1454 mm_segment_t oldfs; 1456 1455 __be32 err; 1457 1456 int host_err; ··· 1462 1463 1463 1464 path.mnt = fhp->fh_export->ex_path.mnt; 1464 1465 path.dentry = fhp->fh_dentry; 1465 - inode = d_inode(path.dentry); 1466 1466 1467 1467 err = nfserr_inval; 1468 - if (!inode->i_op->readlink) 1468 + if (!d_is_symlink(path.dentry)) 1469 1469 goto out; 1470 1470 1471 1471 touch_atime(&path); ··· 1473 1475 */ 1474 1476 1475 1477 oldfs = get_fs(); set_fs(KERNEL_DS); 1476 - host_err = inode->i_op->readlink(path.dentry, (char __user *)buf, *lenp); 1478 + host_err = vfs_readlink(path.dentry, (char __user *)buf, *lenp); 1477 1479 set_fs(oldfs); 1478 1480 1479 1481 if (host_err < 0)
+5 -3
fs/stat.c
··· 329 329 struct inode *inode = d_backing_inode(path.dentry); 330 330 331 331 error = empty ? -ENOENT : -EINVAL; 332 - if (inode->i_op->readlink) { 332 + /* 333 + * AFS mountpoints allow readlink(2) but are not symlinks 334 + */ 335 + if (d_is_symlink(path.dentry) || inode->i_op->readlink) { 333 336 error = security_inode_readlink(path.dentry); 334 337 if (!error) { 335 338 touch_atime(&path); 336 - error = inode->i_op->readlink(path.dentry, 337 - buf, bufsiz); 339 + error = vfs_readlink(path.dentry, buf, bufsiz); 338 340 } 339 341 } 340 342 path_put(&path);
+2 -2
fs/xfs/xfs_ioctl.c
··· 287 287 return PTR_ERR(dentry); 288 288 289 289 /* Restrict this handle operation to symlinks only. */ 290 - if (!d_inode(dentry)->i_op->readlink) { 290 + if (!d_is_symlink(dentry)) { 291 291 error = -EINVAL; 292 292 goto out_dput; 293 293 } ··· 297 297 goto out_dput; 298 298 } 299 299 300 - error = d_inode(dentry)->i_op->readlink(dentry, hreq->ohandle, olen); 300 + error = vfs_readlink(dentry, hreq->ohandle, olen); 301 301 302 302 out_dput: 303 303 dput(dentry);
+1
include/linux/fs.h
··· 2935 2935 extern int vfs_fstat(unsigned int, struct kstat *); 2936 2936 extern int vfs_fstatat(int , const char __user *, struct kstat *, int); 2937 2937 extern const char *vfs_get_link(struct dentry *, struct delayed_call *); 2938 + extern int vfs_readlink(struct dentry *, char __user *, int); 2938 2939 2939 2940 extern int __generic_block_fiemap(struct inode *inode, 2940 2941 struct fiemap_extent_info *fieinfo,