diff options
author | Brian Behlendorf <[email protected]> | 2011-02-16 15:54:55 -0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2011-02-16 15:54:55 -0800 |
commit | 8b4f9a2d55fc5ee28f69b29f2fece7d8e2cb5c7a (patch) | |
tree | 861a08730d642af38bbc891003b1fcc430029464 /module/zfs/zpl_inode.c | |
parent | b9f6a49025809d658754c758dc7e2a81ee41271b (diff) |
Fix readlink(2)
This patch addresses three issues related to symlinks.
1) Revert the zfs_follow_link() function to a modified version
of the original zfs_readlink(). The only changes from the
original OpenSolaris version relate to using Linux types.
For the moment this means no vnode's and no zfsvfs_t. The
caller zpl_follow_link() was also updated accordingly. This
change was reverted because it was slightly gratuitious.
2) Update zpl_follow_link() to use local variables for the
link buffer. I'd forgotten that iov.iov_base is updated by
uiomove() so after the call to zfs_readlink() it can not longer
be used. We need our own private copy of the link pointer.
3) Allocate MAXPATHLEN instead of MAXPATHLEN+1. By default
MAXPATHLEN is 4096 bytes which is a full page, adding one to
it pushes it slightly over a page. That means you'll likely
end up allocating 2 pages which is wasteful of memory and
possibly slightly slower.
Diffstat (limited to 'module/zfs/zpl_inode.c')
-rw-r--r-- | module/zfs/zpl_inode.c | 31 |
1 files changed, 28 insertions, 3 deletions
diff --git a/module/zfs/zpl_inode.c b/module/zfs/zpl_inode.c index 637a7dc69..75d299b47 100644 --- a/module/zfs/zpl_inode.c +++ b/module/zfs/zpl_inode.c @@ -245,8 +245,33 @@ out: static void * zpl_follow_link(struct dentry *dentry, struct nameidata *nd) { - (void) zfs_follow_link(dentry, nd); - return NULL; + struct inode *ip = dentry->d_inode; + struct iovec iov; + uio_t uio; + char *link; + cred_t *cr; + int error; + + cr = (cred_t *)get_current_cred(); + + iov.iov_len = MAXPATHLEN; + iov.iov_base = link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = (MAXPATHLEN - 1); + uio.uio_segflg = UIO_SYSSPACE; + + error = zfs_readlink(ip, &uio, cr); + if (error) { + kmem_free(link, MAXPATHLEN); + nd_set_link(nd, ERR_PTR(error)); + } else { + nd_set_link(nd, link); + } + + put_cred(cr); + return (NULL); } static void @@ -256,7 +281,7 @@ zpl_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr) link = nd_get_link(nd); if (!IS_ERR(link)) - kmem_free(link, MAXPATHLEN + 1); + kmem_free(link, MAXPATHLEN); } static int |