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

Al Viro
2015-04-20T18:13:32Z

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 --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
--
2.1.4