diff mbox

[23/24] new ->follow_link() and ->put_link() calling conventions

Message ID 1429553588-24764-23-git-send-email-viro@ZenIV.linux.org.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Al Viro April 20, 2015, 6:13 p.m. UTC
From: Al Viro <viro@zeniv.linux.org.uk>

a) instead of storing the symlink body (via nd_set_link()) and returning
an opaque pointer later passed to ->put_link(), ->follow_link() _stores_
that opaque pointer (via nd_pin_link()) and returns the symlink body.
Returning ERR_PTR() on error, NULL on jump (procfs magic symlinks) and
pointer to symlink body for normal symlinks.

Storing NULL for opaque pointer (or not storing it at all) means no call
of ->put_link().

b) the body isn't passed to ->put_link() anymore, only the opaque pointer
is.  In the cases when we used the symlink body to free stuff, ->follow_link()
now stores it as opaque pointer as well as returning it.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 Documentation/filesystems/Locking             |  4 +-
 Documentation/filesystems/vfs.txt             |  4 +-
 drivers/staging/lustre/lustre/llite/symlink.c | 11 ++--
 fs/9p/vfs_inode.c                             | 14 +++--
 fs/9p/vfs_inode_dotl.c                        |  6 +-
 fs/autofs4/symlink.c                          |  5 +-
 fs/befs/linuxvfs.c                            | 42 +++++++-------
 fs/ceph/inode.c                               |  6 +-
 fs/cifs/cifsfs.h                              |  2 +-
 fs/cifs/link.c                                | 27 +++++----
 fs/configfs/symlink.c                         | 28 ++++-----
 fs/debugfs/file.c                             |  5 +-
 fs/ecryptfs/inode.c                           |  9 ++-
 fs/exofs/symlink.c                            |  7 +--
 fs/ext2/symlink.c                             |  6 +-
 fs/ext3/symlink.c                             |  6 +-
 fs/ext4/symlink.c                             |  6 +-
 fs/freevxfs/vxfs_immed.c                      |  8 +--
 fs/fuse/dir.c                                 | 19 ++-----
 fs/gfs2/inode.c                               | 10 ++--
 fs/hostfs/hostfs_kern.c                       | 15 +++--
 fs/hppfs/hppfs.c                              |  6 +-
 fs/jffs2/symlink.c                            |  9 +--
 fs/jfs/symlink.c                              |  6 +-
 fs/kernfs/symlink.c                           | 22 ++++----
 fs/libfs.c                                    |  5 +-
 fs/namei.c                                    | 81 +++++++++++----------------
 fs/nfs/symlink.c                              | 18 ++----
 fs/overlayfs/inode.c                          | 17 +++---
 fs/proc/base.c                                |  2 +-
 fs/proc/inode.c                               |  8 +--
 fs/proc/namespaces.c                          |  2 +-
 fs/proc/self.c                                | 24 ++++----
 fs/proc/thread_self.c                         | 22 ++++----
 fs/sysv/symlink.c                             |  5 +-
 fs/ubifs/file.c                               |  7 +--
 fs/ufs/symlink.c                              |  6 +-
 fs/xfs/xfs_iops.c                             |  9 ++-
 include/linux/fs.h                            | 10 ++--
 include/linux/namei.h                         |  3 +-
 mm/shmem.c                                    | 28 +++++----
 41 files changed, 232 insertions(+), 298 deletions(-)
diff mbox

Patch

diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index e4a0c36..f13e9e7 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -50,8 +50,8 @@  prototypes:
 	int (*rename2) (struct inode *, struct dentry *,
 			struct inode *, struct dentry *, unsigned int);
 	int (*readlink) (struct dentry *, char __user *,int);
-	void * (*follow_link) (struct dentry *);
-	void (*put_link) (struct dentry *, char *, void *);
+	const char *(*follow_link) (struct dentry *);
+	void (*put_link) (struct dentry *, void *);
 	void (*truncate) (struct inode *);
 	int (*permission) (struct inode *, int, unsigned int);
 	int (*get_acl)(struct inode *, int);
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 807bd4b..5d61fc8 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -350,8 +350,8 @@  struct inode_operations {
 	int (*rename2) (struct inode *, struct dentry *,
 			struct inode *, struct dentry *, unsigned int);
 	int (*readlink) (struct dentry *, char __user *,int);
-        void * (*follow_link) (struct dentry *);
-	void (*put_link) (struct dentry *, char *, void *);
+        const char *(*follow_link) (struct dentry *);
+	void (*put_link) (struct dentry *, void *);
 	int (*permission) (struct inode *, int);
 	int (*get_acl)(struct inode *, int);
 	int (*setattr) (struct dentry *, struct iattr *);
diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c
index d71c2a9..4f45b47 100644
--- a/drivers/staging/lustre/lustre/llite/symlink.c
+++ b/drivers/staging/lustre/lustre/llite/symlink.c
@@ -118,7 +118,7 @@  failed:
 	return rc;
 }
 
-static void *ll_follow_link(struct dentry *dentry)
+static const char *ll_follow_link(struct dentry *dentry)
 {
 	struct inode *inode = d_inode(dentry);
 	struct ptlrpc_request *request = NULL;
@@ -131,18 +131,17 @@  static void *ll_follow_link(struct dentry *dentry)
 	ll_inode_size_unlock(inode);
 	if (rc) {
 		ptlrpc_req_finished(request);
-		request = NULL;
-		symname = ERR_PTR(rc);
+		return ERR_PTR(rc);
 	}
 
-	nd_set_link(symname);
 	/* symname may contain a pointer to the request message buffer,
 	 * we delay request releasing until ll_put_link then.
 	 */
-	return request;
+	nd_pin_link(request);
+	return symname;
 }
 
-static void ll_put_link(struct dentry *dentry, char *link, void *cookie)
+static void ll_put_link(struct dentry *dentry, void *cookie)
 {
 	ptlrpc_req_finished(cookie);
 }
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 2895295..9cbeb8b 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -1229,11 +1229,12 @@  ino_t v9fs_qid2ino(struct p9_qid *qid)
  *
  */
 
-static void *v9fs_vfs_follow_link(struct dentry *dentry)
+static const char *v9fs_vfs_follow_link(struct dentry *dentry)
 {
 	struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry);
 	struct p9_fid *fid = v9fs_fid_lookup(dentry);
 	struct p9_wstat *st;
+	char *res;
 
 	p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
 
@@ -1252,14 +1253,15 @@  static void *v9fs_vfs_follow_link(struct dentry *dentry)
 		kfree(st);
 		return ERR_PTR(-EINVAL);
 	}
-	if (strlen(st->extension) >= PATH_MAX)
-		st->extension[PATH_MAX - 1] = '\0';
-
-	nd_set_link(st->extension);
+	res = st->extension;
 	st->extension = NULL;
+	if (strlen(res) >= PATH_MAX)
+		res[PATH_MAX - 1] = '\0';
+
 	p9stat_free(st);
 	kfree(st);
-	return NULL;
+	nd_pin_link(res);
+	return res;
 }
 
 /**
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index 577af9b..b708806 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -908,7 +908,7 @@  error:
  *
  */
 
-static void *
+static const char *
 v9fs_vfs_follow_link_dotl(struct dentry *dentry)
 {
 	struct p9_fid *fid = v9fs_fid_lookup(dentry);
@@ -922,8 +922,8 @@  v9fs_vfs_follow_link_dotl(struct dentry *dentry)
 	retval = p9_client_readlink(fid, &target);
 	if (retval)
 		return ERR_PTR(retval);
-	nd_set_link(target);
-	return NULL;
+	nd_pin_link(target);
+	return target;
 }
 
 int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c
index d6dd4c5..a6668fb 100644
--- a/fs/autofs4/symlink.c
+++ b/fs/autofs4/symlink.c
@@ -12,14 +12,13 @@ 
 
 #include "autofs_i.h"
 
-static void *autofs4_follow_link(struct dentry *dentry)
+static const char *autofs4_follow_link(struct dentry *dentry)
 {
 	struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
 	struct autofs_info *ino = autofs4_dentry_ino(dentry);
 	if (ino && !autofs4_oz_mode(sbi))
 		ino->last_used = jiffies;
-	nd_set_link(d_inode(dentry)->i_private);
-	return NULL;
+	return d_inode(dentry)->i_private;
 }
 
 const struct inode_operations autofs4_symlink_inode_operations = {
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 0714644..2521316 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -42,8 +42,8 @@  static struct inode *befs_iget(struct super_block *, unsigned long);
 static struct inode *befs_alloc_inode(struct super_block *sb);
 static void befs_destroy_inode(struct inode *inode);
 static void befs_destroy_inodecache(void);
-static void *befs_follow_link(struct dentry *);
-static void *befs_fast_follow_link(struct dentry *);
+static const char *befs_follow_link(struct dentry *);
+static const char *befs_fast_follow_link(struct dentry *);
 static int befs_utf2nls(struct super_block *sb, const char *in, int in_len,
 			char **out, int *out_len);
 static int befs_nls2utf(struct super_block *sb, const char *in, int in_len,
@@ -468,7 +468,7 @@  befs_destroy_inodecache(void)
  * The data stream become link name. Unless the LONG_SYMLINK
  * flag is set.
  */
-static void *
+static const char *
 befs_follow_link(struct dentry *dentry)
 {
 	struct super_block *sb = dentry->d_sb;
@@ -479,32 +479,28 @@  befs_follow_link(struct dentry *dentry)
 
 	if (len == 0) {
 		befs_error(sb, "Long symlink with illegal length");
-		link = ERR_PTR(-EIO);
-	} else {
-		befs_debug(sb, "Follow long symlink");
-
-		link = kmalloc(len, GFP_NOFS);
-		if (!link) {
-			link = ERR_PTR(-ENOMEM);
-		} else if (befs_read_lsymlink(sb, data, link, len) != len) {
-			kfree(link);
-			befs_error(sb, "Failed to read entire long symlink");
-			link = ERR_PTR(-EIO);
-		} else {
-			link[len - 1] = '\0';
-		}
+		return ERR_PTR(-EIO);
 	}
-	nd_set_link(link);
-	return NULL;
+	befs_debug(sb, "Follow long symlink");
+
+	link = kmalloc(len, GFP_NOFS);
+	if (!link)
+		return ERR_PTR(-ENOMEM);
+	if (befs_read_lsymlink(sb, data, link, len) != len) {
+		kfree(link);
+		befs_error(sb, "Failed to read entire long symlink");
+		return ERR_PTR(-EIO);
+	}
+	link[len - 1] = '\0';
+	nd_pin_link(link);
+	return link;
 }
 
 
-static void *
+static const char *
 befs_fast_follow_link(struct dentry *dentry)
 {
-	befs_inode_info *befs_ino = BEFS_I(d_inode(dentry));
-	nd_set_link(befs_ino->i_data.symlink);
-	return NULL;
+	return BEFS_I(d_inode(dentry))->i_data.symlink;
 }
 
 /*
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 7dd8f1c..5646a9b 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1691,11 +1691,9 @@  retry:
 /*
  * symlinks
  */
-static void *ceph_sym_follow_link(struct dentry *dentry)
+static const char *ceph_sym_follow_link(struct dentry *dentry)
 {
-	struct ceph_inode_info *ci = ceph_inode(d_inode(dentry));
-	nd_set_link(ci->i_symlink);
-	return NULL;
+	return ceph_inode(d_inode(dentry))->i_symlink;
 }
 
 static const struct inode_operations ceph_symlink_iops = {
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index e3a6ef5..cb26cbe 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -120,7 +120,7 @@  extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
 #endif
 
 /* Functions related to symlinks */
-extern void *cifs_follow_link(struct dentry *direntry);
+extern const char *cifs_follow_link(struct dentry *direntry);
 extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
 			 int buflen);
 extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 470666c..3760999 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -626,7 +626,7 @@  cifs_hl_exit:
 	return rc;
 }
 
-void *
+const char *
 cifs_follow_link(struct dentry *direntry)
 {
 	struct inode *inode = d_inode(direntry);
@@ -643,16 +643,18 @@  cifs_follow_link(struct dentry *direntry)
 
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink)) {
-		rc = PTR_ERR(tlink);
-		tlink = NULL;
-		goto out;
+		free_xid(xid);
+		return ERR_CAST(tlink);
 	}
 	tcon = tlink_tcon(tlink);
 	server = tcon->ses->server;
 
 	full_path = build_path_from_dentry(direntry);
-	if (!full_path)
-		goto out;
+	if (!full_path) {
+		free_xid(xid);
+		cifs_put_tlink(tlink);
+		return ERR_PTR(-ENOMEM);
+	}
 
 	cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);
 
@@ -670,17 +672,14 @@  cifs_follow_link(struct dentry *direntry)
 						&target_path, cifs_sb);
 
 	kfree(full_path);
-out:
+	free_xid(xid);
+	cifs_put_tlink(tlink);
 	if (rc != 0) {
 		kfree(target_path);
-		target_path = ERR_PTR(rc);
+		return ERR_PTR(rc);
 	}
-
-	free_xid(xid);
-	if (tlink)
-		cifs_put_tlink(tlink);
-	nd_set_link(target_path);
-	return NULL;
+	nd_pin_link(target_path);
+	return target_path;
 }
 
 int
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c
index ff41712..d2ec994 100644
--- a/fs/configfs/symlink.c
+++ b/fs/configfs/symlink.c
@@ -279,30 +279,26 @@  static int configfs_getlink(struct dentry *dentry, char * path)
 
 }
 
-static void *configfs_follow_link(struct dentry *dentry)
+static const char *configfs_follow_link(struct dentry *dentry)
 {
-	int error = -ENOMEM;
 	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	int error;
 
-	if (page) {
-		error = configfs_getlink(dentry, (char *)page);
-		if (!error) {
-			nd_set_link((char *)page);
-			return (void *)page;
-		}
+	if (!page)
+		return ERR_PTR(-ENOMEM);
+
+	error = configfs_getlink(dentry, (char *)page);
+	if (!error) {
+		nd_pin_link((void *)page);
+		return (char *)page;
 	}
 
-	nd_set_link(ERR_PTR(error));
-	return NULL;
+	return ERR_PTR(error);
 }
 
-static void configfs_put_link(struct dentry *dentry, char *link,
-			      void *cookie)
+static void configfs_put_link(struct dentry *dentry, void *cookie)
 {
-	if (cookie) {
-		unsigned long page = (unsigned long)cookie;
-		free_page(page);
-	}
+	free_page((unsigned long)cookie);
 }
 
 const struct inode_operations configfs_symlink_inode_operations = {
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 4a612f1..914d0cc 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -43,10 +43,9 @@  const struct file_operations debugfs_file_operations = {
 	.llseek =	noop_llseek,
 };
 
-static void *debugfs_follow_link(struct dentry *dentry)
+static const char *debugfs_follow_link(struct dentry *dentry)
 {
-	nd_set_link(d_inode(dentry)->i_private);
-	return NULL;
+	return d_inode(dentry)->i_private;
 }
 
 const struct inode_operations debugfs_link_operations = {
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 8f46945..dc3f17c 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -673,18 +673,17 @@  out:
 	return rc ? ERR_PTR(rc) : buf;
 }
 
-static void *ecryptfs_follow_link(struct dentry *dentry)
+static const char *ecryptfs_follow_link(struct dentry *dentry)
 {
 	size_t len;
 	char *buf = ecryptfs_readlink_lower(dentry, &len);
 	if (IS_ERR(buf))
-		goto out;
+		return buf;
 	fsstack_copy_attr_atime(d_inode(dentry),
 				d_inode(ecryptfs_dentry_to_lower(dentry)));
 	buf[len] = '\0';
-out:
-	nd_set_link(buf);
-	return NULL;
+	nd_pin_link(buf);
+	return buf;
 }
 
 /**
diff --git a/fs/exofs/symlink.c b/fs/exofs/symlink.c
index c4e3db4..279b4d2 100644
--- a/fs/exofs/symlink.c
+++ b/fs/exofs/symlink.c
@@ -35,12 +35,9 @@ 
 
 #include "exofs.h"
 
-static void *exofs_follow_link(struct dentry *dentry)
+static const char *exofs_follow_link(struct dentry *dentry)
 {
-	struct exofs_i_info *oi = exofs_i(d_inode(dentry));
-
-	nd_set_link((char *)oi->i_data);
-	return NULL;
+	return (char *)exofs_i(d_inode(dentry))->i_data;
 }
 
 const struct inode_operations exofs_symlink_inode_operations = {
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index a6b1642..18eeb68 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -21,11 +21,9 @@ 
 #include "xattr.h"
 #include <linux/namei.h>
 
-static void *ext2_follow_link(struct dentry *dentry)
+static const char *ext2_follow_link(struct dentry *dentry)
 {
-	struct ext2_inode_info *ei = EXT2_I(d_inode(dentry));
-	nd_set_link((char *)ei->i_data);
-	return NULL;
+	return (char *)EXT2_I(d_inode(dentry))->i_data;
 }
 
 const struct inode_operations ext2_symlink_inode_operations = {
diff --git a/fs/ext3/symlink.c b/fs/ext3/symlink.c
index dd9763f..0110f84 100644
--- a/fs/ext3/symlink.c
+++ b/fs/ext3/symlink.c
@@ -21,11 +21,9 @@ 
 #include "ext3.h"
 #include "xattr.h"
 
-static void * ext3_follow_link(struct dentry *dentry)
+static const char *ext3_follow_link(struct dentry *dentry)
 {
-	struct ext3_inode_info *ei = EXT3_I(d_inode(dentry));
-	nd_set_link((char*)ei->i_data);
-	return NULL;
+	return (char *)EXT3_I(d_inode(dentry))->i_data;
 }
 
 const struct inode_operations ext3_symlink_inode_operations = {
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index af7dc39..993e42f 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -23,11 +23,9 @@ 
 #include "ext4.h"
 #include "xattr.h"
 
-static void *ext4_follow_link(struct dentry *dentry)
+static const char *ext4_follow_link(struct dentry *dentry)
 {
-	struct ext4_inode_info *ei = EXT4_I(d_inode(dentry));
-	nd_set_link((char *) ei->i_data);
-	return NULL;
+	return (char *)EXT4_I(d_inode(dentry))->i_data;
 }
 
 const struct inode_operations ext4_symlink_inode_operations = {
diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c
index f906f31..f4b0132 100644
--- a/fs/freevxfs/vxfs_immed.c
+++ b/fs/freevxfs/vxfs_immed.c
@@ -39,7 +39,7 @@ 
 #include "vxfs_inode.h"
 
 
-static void *	vxfs_immed_follow_link(struct dentry *);
+static const char *vxfs_immed_follow_link(struct dentry *);
 
 static int	vxfs_immed_readpage(struct file *, struct page *);
 
@@ -72,12 +72,10 @@  const struct address_space_operations vxfs_immed_aops = {
  * Returns:
  *   Zero on success, else a negative error code.
  */
-static void *
+static const char *
 vxfs_immed_follow_link(struct dentry *dp)
 {
-	struct vxfs_inode_info		*vip = VXFS_INO(d_inode(dp));
-	nd_set_link(vip->vii_immed.vi_immed);
-	return NULL;
+	return VXFS_INO(d_inode(dp))->vii_immed.vi_immed;
 }
 
 /**
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 99230de..c098df4 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1365,7 +1365,7 @@  static int fuse_readdir(struct file *file, struct dir_context *ctx)
 	return err;
 }
 
-static char *read_link(struct dentry *dentry)
+static const char *fuse_follow_link(struct dentry *dentry)
 {
 	struct inode *inode = d_inode(dentry);
 	struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1389,26 +1389,15 @@  static char *read_link(struct dentry *dentry)
 		link = ERR_PTR(ret);
 	} else {
 		link[ret] = '\0';
+		nd_pin_link(link);
 	}
 	fuse_invalidate_atime(inode);
 	return link;
 }
 
-static void free_link(char *link)
+static void fuse_put_link(struct dentry *dentry, void *cookie)
 {
-	if (!IS_ERR(link))
-		free_page((unsigned long) link);
-}
-
-static void *fuse_follow_link(struct dentry *dentry)
-{
-	nd_set_link(read_link(dentry));
-	return NULL;
-}
-
-static void fuse_put_link(struct dentry *dentry, char *link, void *c)
-{
-	free_link(link);
+	free_page((unsigned long) cookie);
 }
 
 static int fuse_dir_open(struct inode *inode, struct file *file)
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index b89fa98..62685cc 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1547,7 +1547,7 @@  out:
  * Returns: 0 on success or error code
  */
 
-static void *gfs2_follow_link(struct dentry *dentry)
+static const char *gfs2_follow_link(struct dentry *dentry)
 {
 	struct gfs2_inode *ip = GFS2_I(d_inode(dentry));
 	struct gfs2_holder i_gh;
@@ -1560,8 +1560,7 @@  static void *gfs2_follow_link(struct dentry *dentry)
 	error = gfs2_glock_nq(&i_gh);
 	if (error) {
 		gfs2_holder_uninit(&i_gh);
-		nd_set_link(ERR_PTR(error));
-		return NULL;
+		return ERR_PTR(error);
 	}
 
 	size = (unsigned int)i_size_read(&ip->i_inode);
@@ -1585,8 +1584,9 @@  static void *gfs2_follow_link(struct dentry *dentry)
 	brelse(dibh);
 out:
 	gfs2_glock_dq_uninit(&i_gh);
-	nd_set_link(buf);
-	return NULL;
+	if (!IS_ERR(buf))
+		nd_pin_link(buf);
+	return buf;
 }
 
 /**
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index f80292b..9dcc97a 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -880,7 +880,7 @@  static const struct inode_operations hostfs_dir_iops = {
 	.setattr	= hostfs_setattr,
 };
 
-static void *hostfs_follow_link(struct dentry *dentry)
+static const char *hostfs_follow_link(struct dentry *dentry)
 {
 	char *link = __getname();
 	if (link) {
@@ -894,20 +894,19 @@  static void *hostfs_follow_link(struct dentry *dentry)
 		}
 		if (err < 0) {
 			__putname(link);
-			link = ERR_PTR(err);
+			return ERR_PTR(err);
 		}
 	} else {
-		link = ERR_PTR(-ENOMEM);
+		return ERR_PTR(-ENOMEM);
 	}
 
-	nd_set_link(link);
-	return NULL;
+	nd_pin_link(link);
+	return link;
 }
 
-static void hostfs_put_link(struct dentry *dentry, char *s, void *cookie)
+static void hostfs_put_link(struct dentry *dentry, void *cookie)
 {
-	if (!IS_ERR(s))
-		__putname(s);
+	__putname(cookie);
 }
 
 static const struct inode_operations hostfs_link_iops = {
diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c
index 2620030..969f7e0 100644
--- a/fs/hppfs/hppfs.c
+++ b/fs/hppfs/hppfs.c
@@ -642,19 +642,19 @@  static int hppfs_readlink(struct dentry *dentry, char __user *buffer,
 						    buflen);
 }
 
-static void *hppfs_follow_link(struct dentry *dentry)
+static const char *hppfs_follow_link(struct dentry *dentry)
 {
 	struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry;
 
 	return d_inode(proc_dentry)->i_op->follow_link(proc_dentry);
 }
 
-static void hppfs_put_link(struct dentry *dentry, char *link, void *cookie)
+static void hppfs_put_link(struct dentry *dentry, void *cookie)
 {
 	struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry;
 
 	if (d_inode(proc_dentry)->i_op->put_link)
-		d_inode(proc_dentry)->i_op->put_link(proc_dentry, link, cookie);
+		d_inode(proc_dentry)->i_op->put_link(proc_dentry, cookie);
 }
 
 static const struct inode_operations hppfs_dir_iops = {
diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c
index ec28871..6f0b82e 100644
--- a/fs/jffs2/symlink.c
+++ b/fs/jffs2/symlink.c
@@ -16,7 +16,7 @@ 
 #include <linux/namei.h>
 #include "nodelist.h"
 
-static void *jffs2_follow_link(struct dentry *dentry);
+static const char *jffs2_follow_link(struct dentry *dentry);
 
 const struct inode_operations jffs2_symlink_inode_operations =
 {
@@ -29,7 +29,7 @@  const struct inode_operations jffs2_symlink_inode_operations =
 	.removexattr =	jffs2_removexattr
 };
 
-static void *jffs2_follow_link(struct dentry *dentry)
+static const char *jffs2_follow_link(struct dentry *dentry)
 {
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode(dentry));
 	char *p = (char *)f->target;
@@ -54,13 +54,10 @@  static void *jffs2_follow_link(struct dentry *dentry)
 	jffs2_dbg(1, "%s(): target path is '%s'\n",
 		  __func__, (char *)f->target);
 
-	nd_set_link(p);
-
 	/*
 	 * We will unlock the f->sem mutex but VFS will use the f->target string. This is safe
 	 * since the only way that may cause f->target to be changed is iput() operation.
 	 * But VFS will not use f->target after iput() has been called.
 	 */
-	return NULL;
+	return p;
 }
-
diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c
index 4299a3c..71f8c31 100644
--- a/fs/jfs/symlink.c
+++ b/fs/jfs/symlink.c
@@ -22,11 +22,9 @@ 
 #include "jfs_inode.h"
 #include "jfs_xattr.h"
 
-static void *jfs_follow_link(struct dentry *dentry)
+static const char *jfs_follow_link(struct dentry *dentry)
 {
-	char *s = JFS_IP(d_inode(dentry))->i_inline;
-	nd_set_link(s);
-	return NULL;
+	return JFS_IP(d_inode(dentry))->i_inline;
 }
 
 const struct inode_operations jfs_fast_symlink_inode_operations = {
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index 63d08ec..f831629 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -112,24 +112,24 @@  static int kernfs_getlink(struct dentry *dentry, char *path)
 	return error;
 }
 
-static void *kernfs_iop_follow_link(struct dentry *dentry)
+static const char *kernfs_iop_follow_link(struct dentry *dentry)
 {
 	int error = -ENOMEM;
 	unsigned long page = get_zeroed_page(GFP_KERNEL);
-	if (page) {
-		error = kernfs_getlink(dentry, (char *) page);
-		if (error < 0)
-			free_page((unsigned long)page);
+	if (!page)
+		return ERR_PTR(-ENOMEM);
+	error = kernfs_getlink(dentry, (char *)page);
+	if (unlikely(error < 0)) {
+		free_page((unsigned long)page);
+		return ERR_PTR(error);
 	}
-	nd_set_link(error ? ERR_PTR(error) : (char *)page);
-	return NULL;
+	nd_pin_link((void *)page);
+	return (char *)page;
 }
 
-static void kernfs_iop_put_link(struct dentry *dentry, char *page,
-				void *cookie)
+static void kernfs_iop_put_link(struct dentry *dentry, void *cookie)
 {
-	if (!IS_ERR(page))
-		free_page((unsigned long)page);
+	free_page((unsigned long)cookie);
 }
 
 const struct inode_operations kernfs_symlink_iops = {
diff --git a/fs/libfs.c b/fs/libfs.c
index fe6041a..c6955d3 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1024,10 +1024,9 @@  int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 }
 EXPORT_SYMBOL(noop_fsync);
 
-void kfree_put_link(struct dentry *dentry, char *s, void *cookie)
+void kfree_put_link(struct dentry *dentry, void *cookie)
 {
-	if (!IS_ERR(s))
-		kfree(s);
+	kfree(cookie);
 }
 EXPORT_SYMBOL(kfree_put_link);
 
diff --git a/fs/namei.c b/fs/namei.c
index 425986a..f1ec430 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -508,7 +508,6 @@  struct nameidata {
 		struct path link;
 		const char *name;
 		void *cookie;
-		char *body;
 	} *stack;
 };
 
@@ -739,26 +738,27 @@  void nd_jump_link(struct path *path)
 	nd->flags |= LOOKUP_JUMPED;
 }
 
-void nd_set_link(char *path)
+void nd_pin_link(void *cookie)
 {
 	struct nameidata *nd = current->nameidata;
 
-	nd->stack[nd->depth].body = path;
+	nd->stack[nd->depth].cookie = cookie;
 }
-EXPORT_SYMBOL(nd_set_link);
+EXPORT_SYMBOL(nd_pin_link);
 
-static inline char *nd_get_link(struct nameidata *nd)
+void *nd_pinned_link(void)
 {
-	return nd->stack[nd->depth].body;
+	struct nameidata *nd = current->nameidata;
+	return nd->stack[nd->depth].cookie;
 }
+EXPORT_SYMBOL(nd_pinned_link);
 
 static inline void put_link(struct nameidata *nd)
 {
 	struct saved *last = nd->stack + nd->depth;
 	struct inode *inode = last->link.dentry->d_inode;
-	if (inode->i_op->put_link)
-		inode->i_op->put_link(last->link.dentry, last->body,
-				      last->cookie);
+	if (last->cookie && inode->i_op->put_link)
+		inode->i_op->put_link(last->link.dentry, last->cookie);
 	path_put(&last->link);
 }
 
@@ -878,14 +878,14 @@  static int may_linkat(struct path *link)
 	return -EPERM;
 }
 
-static __always_inline char *get_link(struct nameidata *nd, struct path *next)
+static __always_inline
+const char *get_link(struct nameidata *nd, struct path *next)
 {
 	struct saved *last = nd->stack + nd->depth;
 	struct dentry *dentry = next->dentry;
 	struct inode *inode = dentry->d_inode;
-	void *cookie;
 	int error;
-	char *res;
+	const char *res;
 
 	BUG_ON(nd->flags & LOOKUP_RCU);
 
@@ -893,6 +893,7 @@  static __always_inline char *get_link(struct nameidata *nd, struct path *next)
 		mntget(next->mnt);
 
 	last->link = *next;
+	last->cookie = NULL;
 
 	res = ERR_PTR(-ELOOP);
 	if (unlikely(nd->total_link_count >= 40))
@@ -902,7 +903,6 @@  static __always_inline char *get_link(struct nameidata *nd, struct path *next)
 	nd->total_link_count++;
 
 	touch_atime(&last->link);
-	nd_set_link(NULL);
 
 	error = security_inode_follow_link(dentry);
 	res = ERR_PTR(error);
@@ -910,28 +910,18 @@  static __always_inline char *get_link(struct nameidata *nd, struct path *next)
 		goto out;
 
 	nd->last_type = LAST_BIND;
-	res = cookie = inode->i_op->follow_link(dentry);
-	if (IS_ERR(cookie))
-		goto out;
-
-	res = nd_get_link(nd);
-	if (!IS_ERR(res)) {
-		last->cookie = cookie;
-		return res;
-	}
-
-	if (inode->i_op->put_link)
-		inode->i_op->put_link(dentry, res, cookie);
+	res = inode->i_op->follow_link(dentry);
+	if (IS_ERR(res)) {
 out:
-	last->cookie = NULL;
-	path_put(&nd->path);
-	path_put(&last->link);
+		path_put(&nd->path);
+		path_put(&last->link);
+	}
 	return res;
 }
 
 static int follow_link(struct nameidata *nd, struct path *link)
 {
-	char *s;
+	const char *s;
 	int error = may_follow_link(link, nd);
 	if (unlikely(error))
 		return error;
@@ -1845,7 +1835,7 @@  Walked:
 			goto Err;
 
 		if (err) {
-			char *s;
+			const char *s;
 
 			if (unlikely(nd->link_count >= MAX_NESTED_LINKS)) {
 				path_put_conditional(&next, nd);
@@ -4494,21 +4484,19 @@  int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 {
 	struct saved stack;
 	struct nameidata nd, *saved;
-	void *cookie;
+	const char *link;
 	int res;
 
 	nd.depth = 0;
 	nd.stack = &stack;
 	saved = set_nameidata(&nd);
-	cookie = dentry->d_inode->i_op->follow_link(dentry);
-	if (IS_ERR(cookie))
-		res = PTR_ERR(cookie);
-	else {
-		char *link = nd_get_link(&nd);
-
+	link = dentry->d_inode->i_op->follow_link(dentry);
+	if (IS_ERR(link)) {
+		res = PTR_ERR(link);
+	} else {
 		res = readlink_copy(buffer, buflen, link);
 		if (dentry->d_inode->i_op->put_link)
-			dentry->d_inode->i_op->put_link(dentry, link, cookie);
+			dentry->d_inode->i_op->put_link(dentry, stack.cookie);
 	}
 	set_nameidata(saved);
 	return res;
@@ -4542,22 +4530,21 @@  int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 }
 EXPORT_SYMBOL(page_readlink);
 
-void *page_follow_link_light(struct dentry *dentry)
+const char *page_follow_link_light(struct dentry *dentry)
 {
 	struct page *page = NULL;
-	nd_set_link(page_getlink(dentry, &page));
-	return page;
+	char *res = page_getlink(dentry, &page);
+	if (!IS_ERR(res))
+		nd_pin_link(page);
+	return res;
 }
 EXPORT_SYMBOL(page_follow_link_light);
 
-void page_put_link(struct dentry *dentry, char *link, void *cookie)
+void page_put_link(struct dentry *dentry, void *cookie)
 {
 	struct page *page = cookie;
-
-	if (page) {
-		kunmap(page);
-		page_cache_release(page);
-	}
+	kunmap(page);
+	page_cache_release(page);
 }
 EXPORT_SYMBOL(page_put_link);
 
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 1394f87..62879f2 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -43,7 +43,7 @@  error:
 	return -EIO;
 }
 
-static void *nfs_follow_link(struct dentry *dentry)
+static const char *nfs_follow_link(struct dentry *dentry)
 {
 	struct inode *inode = d_inode(dentry);
 	struct page *page;
@@ -51,19 +51,13 @@  static void *nfs_follow_link(struct dentry *dentry)
 
 	err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));
 	if (err)
-		goto read_failed;
+		return err;
 	page = read_cache_page(&inode->i_data, 0,
 				(filler_t *)nfs_symlink_filler, inode);
-	if (IS_ERR(page)) {
-		err = page;
-		goto read_failed;
-	}
-	nd_set_link(kmap(page));
-	return page;
-
-read_failed:
-	nd_set_link(err);
-	return NULL;
+	if (IS_ERR(page))
+		return ERR_CAST(page);
+	nd_pin_link(page);
+	return kmap(page);
 }
 
 /*
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 0de7b87..6efb23e 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -10,6 +10,7 @@ 
 #include <linux/fs.h>
 #include <linux/slab.h>
 #include <linux/xattr.h>
+#include <linux/namei.h>
 #include "overlayfs.h"
 
 static int ovl_copy_up_last(struct dentry *dentry, struct iattr *attr,
@@ -140,12 +141,12 @@  struct ovl_link_data {
 	void *cookie;
 };
 
-static void *ovl_follow_link(struct dentry *dentry)
+static const char *ovl_follow_link(struct dentry *dentry)
 {
-	void *ret;
 	struct dentry *realdentry;
 	struct inode *realinode;
 	struct ovl_link_data *data = NULL;
+	const char *ret;
 
 	realdentry = ovl_dentry_real(dentry);
 	realinode = realdentry->d_inode;
@@ -161,18 +162,20 @@  static void *ovl_follow_link(struct dentry *dentry)
 	}
 
 	ret = realinode->i_op->follow_link(realdentry);
-	if (IS_ERR(ret)) {
+	if (IS_ERR_OR_NULL(ret)) {
 		kfree(data);
 		return ret;
 	}
 
 	if (data)
-		data->cookie = ret;
+		data->cookie = nd_pinned_link();
 
-	return data;
+	nd_pin_link(data);
+
+	return ret;
 }
 
-static void ovl_put_link(struct dentry *dentry, char *link, void *c)
+static void ovl_put_link(struct dentry *dentry, void *c)
 {
 	struct inode *realinode;
 	struct ovl_link_data *data = c;
@@ -181,7 +184,7 @@  static void ovl_put_link(struct dentry *dentry, char *link, void *c)
 		return;
 
 	realinode = data->realdentry->d_inode;
-	realinode->i_op->put_link(data->realdentry, link, data->cookie);
+	realinode->i_op->put_link(data->realdentry, data->cookie);
 	kfree(data);
 }
 
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a511738..7f55e66 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1371,7 +1371,7 @@  static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
 		return -ENOENT;
 }
 
-static void *proc_pid_follow_link(struct dentry *dentry)
+static const char *proc_pid_follow_link(struct dentry *dentry)
 {
 	struct inode *inode = d_inode(dentry);
 	struct path path;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 68934dd..a40e792 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -394,16 +394,16 @@  static const struct file_operations proc_reg_file_ops_no_compat = {
 };
 #endif
 
-static void *proc_follow_link(struct dentry *dentry)
+static const char *proc_follow_link(struct dentry *dentry)
 {
 	struct proc_dir_entry *pde = PDE(d_inode(dentry));
 	if (unlikely(!use_pde(pde)))
 		return ERR_PTR(-EINVAL);
-	nd_set_link(pde->data);
-	return pde;
+	nd_pin_link(pde);
+	return pde->data;
 }
 
-static void proc_put_link(struct dentry *dentry, char *link, void *p)
+static void proc_put_link(struct dentry *dentry, void *p)
 {
 	unuse_pde(p);
 }
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 15852e1..bc9c3dd 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -30,7 +30,7 @@  static const struct proc_ns_operations *ns_entries[] = {
 	&mntns_operations,
 };
 
-static void *proc_ns_follow_link(struct dentry *dentry)
+static const char *proc_ns_follow_link(struct dentry *dentry)
 {
 	struct inode *inode = d_inode(dentry);
 	const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
diff --git a/fs/proc/self.c b/fs/proc/self.c
index 51ed086..eb966da 100644
--- a/fs/proc/self.c
+++ b/fs/proc/self.c
@@ -19,21 +19,21 @@  static int proc_self_readlink(struct dentry *dentry, char __user *buffer,
 	return readlink_copy(buffer, buflen, tmp);
 }
 
-static void *proc_self_follow_link(struct dentry *dentry)
+static const char *proc_self_follow_link(struct dentry *dentry)
 {
 	struct pid_namespace *ns = dentry->d_sb->s_fs_info;
 	pid_t tgid = task_tgid_nr_ns(current, ns);
-	char *name = ERR_PTR(-ENOENT);
-	if (tgid) {
-		/* 11 for max length of signed int in decimal + NULL term */
-		name = kmalloc(12, GFP_KERNEL);
-		if (!name)
-			name = ERR_PTR(-ENOMEM);
-		else
-			sprintf(name, "%d", tgid);
-	}
-	nd_set_link(name);
-	return NULL;
+	char *name;
+
+	if (!tgid)
+		return ERR_PTR(-ENOENT);
+	/* 11 for max length of signed int in decimal + NULL term */
+	name = kmalloc(12, GFP_KERNEL);
+	if (!name)
+		return ERR_PTR(-ENOMEM);
+	sprintf(name, "%d", tgid);
+	nd_pin_link(name);
+	return name;
 }
 
 static const struct inode_operations proc_self_inode_operations = {
diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c
index 60457f6..95e75c2 100644
--- a/fs/proc/thread_self.c
+++ b/fs/proc/thread_self.c
@@ -20,21 +20,21 @@  static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer,
 	return readlink_copy(buffer, buflen, tmp);
 }
 
-static void *proc_thread_self_follow_link(struct dentry *dentry)
+static const char *proc_thread_self_follow_link(struct dentry *dentry)
 {
 	struct pid_namespace *ns = dentry->d_sb->s_fs_info;
 	pid_t tgid = task_tgid_nr_ns(current, ns);
 	pid_t pid = task_pid_nr_ns(current, ns);
-	char *name = ERR_PTR(-ENOENT);
-	if (pid) {
-		name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL);
-		if (!name)
-			name = ERR_PTR(-ENOMEM);
-		else
-			sprintf(name, "%d/task/%d", tgid, pid);
-	}
-	nd_set_link(name);
-	return NULL;
+	char *name;
+
+	if (!pid)
+		return ERR_PTR(-ENOENT);
+	name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL);
+	if (!name)
+		return ERR_PTR(-ENOMEM);
+	sprintf(name, "%d/task/%d", tgid, pid);
+	nd_pin_link(name);
+	return name;
 }
 
 static const struct inode_operations proc_thread_self_inode_operations = {
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
index 6af76f1..6d30ad0 100644
--- a/fs/sysv/symlink.c
+++ b/fs/sysv/symlink.c
@@ -8,10 +8,9 @@ 
 #include "sysv.h"
 #include <linux/namei.h>
 
-static void *sysv_follow_link(struct dentry *dentry)
+static const char *sysv_follow_link(struct dentry *dentry)
 {
-	nd_set_link((char *)SYSV_I(d_inode(dentry))->i_data);
-	return NULL;
+	return (char *)SYSV_I(d_inode(dentry))->i_data;
 }
 
 const struct inode_operations sysv_fast_symlink_inode_operations = {
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index c8ea213..4767a53a 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1299,12 +1299,9 @@  static void ubifs_invalidatepage(struct page *page, unsigned int offset,
 	ClearPageChecked(page);
 }
 
-static void *ubifs_follow_link(struct dentry *dentry)
+static const char *ubifs_follow_link(struct dentry *dentry)
 {
-	struct ubifs_inode *ui = ubifs_inode(d_inode(dentry));
-
-	nd_set_link(ui->data);
-	return NULL;
+	return ubifs_inode(d_inode(dentry))->data;
 }
 
 int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c
index dd34c35..58f8541 100644
--- a/fs/ufs/symlink.c
+++ b/fs/ufs/symlink.c
@@ -32,11 +32,9 @@ 
 #include "ufs.h"
 
 
-static void *ufs_follow_link(struct dentry *dentry)
+static const char *ufs_follow_link(struct dentry *dentry)
 {
-	struct ufs_inode_info *p = UFS_I(d_inode(dentry));
-	nd_set_link((char*)p->i_u1.i_symlink);
-	return NULL;
+	return (char *)UFS_I(d_inode(dentry))->i_u1.i_symlink;
 }
 
 const struct inode_operations ufs_fast_symlink_inode_operations = {
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 1c3df6b..b8cfa9d 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -409,7 +409,7 @@  xfs_vn_rename(
  * we need to be very careful about how much stack we use.
  * uio is kmalloced for this reason...
  */
-STATIC void *
+STATIC const char *
 xfs_vn_follow_link(
 	struct dentry		*dentry)
 {
@@ -424,14 +424,13 @@  xfs_vn_follow_link(
 	if (unlikely(error))
 		goto out_kfree;
 
-	nd_set_link(link);
-	return NULL;
+	nd_pin_link(link);
+	return link;
 
  out_kfree:
 	kfree(link);
  out_err:
-	nd_set_link(ERR_PTR(error));
-	return NULL;
+	return ERR_PTR(error);
 }
 
 STATIC int
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a892fb7..691efe4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1598,12 +1598,12 @@  struct file_operations {
 
 struct inode_operations {
 	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
-	void * (*follow_link) (struct dentry *);
+	const char * (*follow_link) (struct dentry *);
 	int (*permission) (struct inode *, int);
 	struct posix_acl * (*get_acl)(struct inode *, int);
 
 	int (*readlink) (struct dentry *, char __user *,int);
-	void (*put_link) (struct dentry *, char *, void *);
+	void (*put_link) (struct dentry *, void *);
 
 	int (*create) (struct inode *,struct dentry *, umode_t, bool);
 	int (*link) (struct dentry *,struct inode *,struct dentry *);
@@ -2694,13 +2694,13 @@  extern const struct file_operations generic_ro_fops;
 
 extern int readlink_copy(char __user *, int, const char *);
 extern int page_readlink(struct dentry *, char __user *, int);
-extern void *page_follow_link_light(struct dentry *);
-extern void page_put_link(struct dentry *, char *, void *);
+extern const char *page_follow_link_light(struct dentry *);
+extern void page_put_link(struct dentry *, void *);
 extern int __page_symlink(struct inode *inode, const char *symname, int len,
 		int nofs);
 extern int page_symlink(struct inode *inode, const char *symname, int len);
 extern const struct inode_operations page_symlink_inode_operations;
-extern void kfree_put_link(struct dentry *, char *, void *);
+extern void kfree_put_link(struct dentry *, 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);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index cc8b51a..3a06d96 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -70,7 +70,8 @@  extern struct dentry *lock_rename(struct dentry *, struct dentry *);
 extern void unlock_rename(struct dentry *, struct dentry *);
 
 extern void nd_jump_link(struct path *path);
-extern void nd_set_link(char *path);
+extern void nd_pin_link(void *cookie);
+extern void *nd_pinned_link(void);
 
 static inline void nd_terminate_link(void *name, size_t len, size_t maxlen)
 {
diff --git a/mm/shmem.c b/mm/shmem.c
index daad5af..ed19f64 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2474,30 +2474,28 @@  static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
 	return 0;
 }
 
-static void *shmem_follow_short_symlink(struct dentry *dentry)
+static const char *shmem_follow_short_symlink(struct dentry *dentry)
 {
-	nd_set_link(SHMEM_I(d_inode(dentry))->symlink);
-	return NULL;
+	return SHMEM_I(d_inode(dentry))->symlink;
 }
 
-static void *shmem_follow_link(struct dentry *dentry)
+static const char *shmem_follow_link(struct dentry *dentry)
 {
 	struct page *page = NULL;
 	int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL);
-	nd_set_link(error ? ERR_PTR(error) : kmap(page));
-	if (page)
-		unlock_page(page);
-	return page;
+	if (error)
+		return ERR_PTR(error);
+	unlock_page(page);
+	nd_pin_link(page);
+	return kmap(page);
 }
 
-static void shmem_put_link(struct dentry *dentry, char *link, void *cookie)
+static void shmem_put_link(struct dentry *dentry, void *cookie)
 {
-	if (!IS_ERR(link)) {
-		struct page *page = cookie;
-		kunmap(page);
-		mark_page_accessed(page);
-		page_cache_release(page);
-	}
+	struct page *page = cookie;
+	kunmap(page);
+	mark_page_accessed(page);
+	page_cache_release(page);
 }
 
 #ifdef CONFIG_TMPFS_XATTR