From: Al Viro <viro@xxxxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html