summaryrefslogtreecommitdiffstats
path: root/module/zfs/zpl_inode.c
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2011-02-16 15:54:55 -0800
committerBrian Behlendorf <[email protected]>2011-02-16 15:54:55 -0800
commit8b4f9a2d55fc5ee28f69b29f2fece7d8e2cb5c7a (patch)
tree861a08730d642af38bbc891003b1fcc430029464 /module/zfs/zpl_inode.c
parentb9f6a49025809d658754c758dc7e2a81ee41271b (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.c31
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