From 21fc61c73c3903c4c312d0802da01ec2b323d174 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 17 Nov 2015 01:07:57 -0500 Subject: don't put symlink bodies in pagecache into highmem kmap() in page_follow_link_light() needed to go - allowing to hold an arbitrary number of kmaps for long is a great way to deadlocking the system. new helper (inode_nohighmem(inode)) needs to be used for pagecache symlinks inodes; done for all in-tree cases. page_follow_link_light() instrumented to yell about anything missed. Signed-off-by: Al Viro --- mm/shmem.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'mm/shmem.c') diff --git a/mm/shmem.c b/mm/shmem.c index 9187eee4128b..64bf5acb49fe 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2444,7 +2444,6 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s int len; struct inode *inode; struct page *page; - char *kaddr; struct shmem_inode_info *info; len = strlen(symname) + 1; @@ -2483,9 +2482,8 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s } inode->i_mapping->a_ops = &shmem_aops; inode->i_op = &shmem_symlink_inode_operations; - kaddr = kmap_atomic(page); - memcpy(kaddr, symname, len); - kunmap_atomic(kaddr); + inode_nohighmem(inode); + memcpy(page_address(page), symname, len); SetPageUptodate(page); set_page_dirty(page); unlock_page(page); @@ -2506,13 +2504,12 @@ static const char *shmem_follow_link(struct dentry *dentry, void **cookie) return ERR_PTR(error); unlock_page(page); *cookie = page; - return kmap(page); + return page_address(page); } static void shmem_put_link(struct inode *unused, void *cookie) { struct page *page = cookie; - kunmap(page); mark_page_accessed(page); page_cache_release(page); } -- cgit v1.2.3 From 6b2553918d8b4e6de9853fd6315bec7271a2e592 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 17 Nov 2015 10:20:54 -0500 Subject: replace ->follow_link() with new method that could stay in RCU mode new method: ->get_link(); replacement of ->follow_link(). The differences are: * inode and dentry are passed separately * might be called both in RCU and non-RCU mode; the former is indicated by passing it a NULL dentry. * when called that way it isn't allowed to block and should return ERR_PTR(-ECHILD) if it needs to be called in non-RCU mode. It's a flagday change - the old method is gone, all in-tree instances converted. Conversion isn't hard; said that, so far very few instances do not immediately bail out when called in RCU mode. That'll change in the next commits. Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 4 +-- Documentation/filesystems/porting | 6 ++++ drivers/staging/lustre/lustre/llite/symlink.c | 8 +++-- fs/9p/vfs_inode.c | 17 +++++++--- fs/9p/vfs_inode_dotl.c | 14 ++++++--- fs/affs/symlink.c | 4 +-- fs/autofs4/symlink.c | 13 +++++--- fs/btrfs/inode.c | 2 +- fs/ceph/inode.c | 2 +- fs/cifs/cifsfs.c | 2 +- fs/cifs/cifsfs.h | 4 +-- fs/cifs/link.c | 6 ++-- fs/coda/cnode.c | 2 +- fs/configfs/symlink.c | 11 +++++-- fs/dcache.c | 2 +- fs/ecryptfs/inode.c | 12 +++++-- fs/ext2/symlink.c | 4 +-- fs/ext4/symlink.c | 13 +++++--- fs/f2fs/namei.c | 16 ++++++---- fs/fuse/dir.c | 9 ++++-- fs/gfs2/inode.c | 15 ++++++--- fs/hostfs/hostfs_kern.c | 10 ++++-- fs/jffs2/symlink.c | 2 +- fs/jfs/symlink.c | 4 +-- fs/kernfs/symlink.c | 11 +++++-- fs/libfs.c | 9 +++--- fs/minix/inode.c | 2 +- fs/namei.c | 45 +++++++++++++++++---------- fs/ncpfs/inode.c | 2 +- fs/nfs/symlink.c | 9 ++++-- fs/nilfs2/namei.c | 2 +- fs/ocfs2/symlink.c | 2 +- fs/overlayfs/inode.c | 12 ++++--- fs/proc/base.c | 22 +++++++------ fs/proc/inode.c | 7 +++-- fs/proc/namespaces.c | 9 ++++-- fs/proc/self.c | 9 ++++-- fs/proc/thread_self.c | 9 ++++-- fs/reiserfs/namei.c | 2 +- fs/squashfs/symlink.c | 2 +- fs/sysv/inode.c | 2 +- fs/ubifs/file.c | 2 +- fs/xfs/xfs_iops.c | 8 +++-- include/linux/fs.h | 6 ++-- mm/shmem.c | 12 ++++--- 45 files changed, 234 insertions(+), 132 deletions(-) (limited to 'mm/shmem.c') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 06d443450f21..4fba54b9fcec 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -50,7 +50,7 @@ prototypes: int (*rename2) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); - const char *(*follow_link) (struct dentry *, void **); + const char *(*get_link) (struct dentry *, struct inode *, void **); void (*put_link) (struct inode *, void *); void (*truncate) (struct inode *); int (*permission) (struct inode *, int, unsigned int); @@ -83,7 +83,7 @@ rmdir: yes (both) (see below) rename: yes (all) (see below) rename2: yes (all) (see below) readlink: no -follow_link: no +get_link: no put_link: no setattr: yes permission: no (may not block if called in rcu-walk mode) diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 3eb7c35c9698..cf92a8c55594 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -509,3 +509,9 @@ in your dentry operations instead. any symlink that might use page_follow_link_light/page_put_link() must have inode_nohighmem(inode) called before anything might start playing with its pagecache. +-- +[mandatory] + ->follow_link() is replaced with ->get_link(); same API, except that + * ->get_link() gets inode as a separate argument + * ->get_link() may be called in RCU mode - in that case NULL + dentry is passed diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c index 69b203651905..153fdf908328 100644 --- a/drivers/staging/lustre/lustre/llite/symlink.c +++ b/drivers/staging/lustre/lustre/llite/symlink.c @@ -118,12 +118,14 @@ failed: return rc; } -static const char *ll_follow_link(struct dentry *dentry, void **cookie) +static const char *ll_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct inode *inode = d_inode(dentry); struct ptlrpc_request *request = NULL; int rc; char *symname = NULL; + if (!dentry) + return ERR_PTR(-ECHILD); CDEBUG(D_VFSTRACE, "VFS Op\n"); ll_inode_size_lock(inode); @@ -149,7 +151,7 @@ static void ll_put_link(struct inode *unused, void *cookie) struct inode_operations ll_fast_symlink_inode_operations = { .readlink = generic_readlink, .setattr = ll_setattr, - .follow_link = ll_follow_link, + .get_link = ll_get_link, .put_link = ll_put_link, .getattr = ll_getattr, .permission = ll_inode_permission, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 699941e90667..8ba5a897fc0a 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1223,18 +1223,25 @@ ino_t v9fs_qid2ino(struct p9_qid *qid) } /** - * v9fs_vfs_follow_link - follow a symlink path + * v9fs_vfs_get_link - follow a symlink path * @dentry: dentry for symlink + * @inode: inode for symlink * @cookie: place to pass the data to put_link() */ -static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie) +static const char *v9fs_vfs_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); - struct p9_fid *fid = v9fs_fid_lookup(dentry); + struct v9fs_session_info *v9ses; + struct p9_fid *fid; struct p9_wstat *st; char *res; + if (!dentry) + return ERR_PTR(-ECHILD); + + v9ses = v9fs_dentry2v9ses(dentry); + fid = v9fs_fid_lookup(dentry); p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); if (IS_ERR(fid)) @@ -1452,7 +1459,7 @@ static const struct inode_operations v9fs_file_inode_operations = { static const struct inode_operations v9fs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = v9fs_vfs_follow_link, + .get_link = v9fs_vfs_get_link, .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr, .setattr = v9fs_vfs_setattr, diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index cb899af1babc..0cc105d804dd 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -899,20 +899,26 @@ error: } /** - * v9fs_vfs_follow_link_dotl - follow a symlink path + * v9fs_vfs_get_link_dotl - follow a symlink path * @dentry: dentry for symlink + * @inode: inode for symlink * @cookie: place to pass the data to put_link() */ static const char * -v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie) +v9fs_vfs_get_link_dotl(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct p9_fid *fid = v9fs_fid_lookup(dentry); + struct p9_fid *fid; char *target; int retval; + if (!dentry) + return ERR_PTR(-ECHILD); + p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); + fid = v9fs_fid_lookup(dentry); if (IS_ERR(fid)) return ERR_CAST(fid); retval = p9_client_readlink(fid, &target); @@ -984,7 +990,7 @@ const struct inode_operations v9fs_file_inode_operations_dotl = { const struct inode_operations v9fs_symlink_inode_operations_dotl = { .readlink = generic_readlink, - .follow_link = v9fs_vfs_follow_link_dotl, + .get_link = v9fs_vfs_get_link_dotl, .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index e3f9dc3ae8cc..39d1194445e1 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -20,7 +20,7 @@ static int affs_symlink_readpage(struct file *file, struct page *page) char c; char lc; - pr_debug("follow_link(ino=%lu)\n", inode->i_ino); + pr_debug("get_link(ino=%lu)\n", inode->i_ino); bh = affs_bread(inode->i_sb, inode->i_ino); if (!bh) @@ -71,7 +71,7 @@ const struct address_space_operations affs_symlink_aops = { const struct inode_operations affs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = affs_notify_change, }; diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c index da0c33481bc0..39e6f0bdf8e3 100644 --- a/fs/autofs4/symlink.c +++ b/fs/autofs4/symlink.c @@ -12,10 +12,15 @@ #include "autofs_i.h" -static const char *autofs4_follow_link(struct dentry *dentry, void **cookie) +static const char *autofs4_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); - struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct autofs_sb_info *sbi; + struct autofs_info *ino; + if (!dentry) + return ERR_PTR(-ECHILD); + sbi = autofs4_sbi(dentry->d_sb); + ino = autofs4_dentry_ino(dentry); if (ino && !autofs4_oz_mode(sbi)) ino->last_used = jiffies; return d_inode(dentry)->i_private; @@ -23,5 +28,5 @@ static const char *autofs4_follow_link(struct dentry *dentry, void **cookie) const struct inode_operations autofs4_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = autofs4_follow_link + .get_link = autofs4_get_link }; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 70f98bfde277..3d4aa69f1e0c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10096,7 +10096,7 @@ static const struct inode_operations btrfs_special_inode_operations = { }; static const struct inode_operations btrfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getattr = btrfs_getattr, .setattr = btrfs_setattr, diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 498dcfa2dcdb..da55eb8bcffa 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1756,7 +1756,7 @@ retry: */ static const struct inode_operations ceph_symlink_iops = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ceph_setattr, .getattr = ceph_getattr, .setxattr = ceph_setxattr, diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index cbc0f4bca0c0..4593f41678ef 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -900,7 +900,7 @@ const struct inode_operations cifs_file_inode_ops = { const struct inode_operations cifs_symlink_inode_ops = { .readlink = generic_readlink, - .follow_link = cifs_follow_link, + .get_link = cifs_get_link, .put_link = kfree_put_link, .permission = cifs_permission, /* BB add the following two eventually */ diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index c3cc1609025f..6886328cf3c4 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -120,9 +120,7 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path); #endif /* Functions related to symlinks */ -extern const char *cifs_follow_link(struct dentry *direntry, void **cookie); -extern int cifs_readlink(struct dentry *direntry, char __user *buffer, - int buflen); +extern const char *cifs_get_link(struct dentry *, struct inode *, void **); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname); extern int cifs_removexattr(struct dentry *, const char *); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index e3548f73bdea..6f2439b508b5 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -627,9 +627,8 @@ cifs_hl_exit: } const char * -cifs_follow_link(struct dentry *direntry, void **cookie) +cifs_get_link(struct dentry *direntry, struct inode *inode, void **cookie) { - struct inode *inode = d_inode(direntry); int rc = -ENOMEM; unsigned int xid; char *full_path = NULL; @@ -639,6 +638,9 @@ cifs_follow_link(struct dentry *direntry, void **cookie) struct cifs_tcon *tcon; struct TCP_Server_Info *server; + if (!direntry) + return ERR_PTR(-ECHILD); + xid = get_xid(); tlink = cifs_sb_tlink(cifs_sb); diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index dd6a79ef4750..f18139c7690a 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -18,7 +18,7 @@ static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2) static const struct inode_operations coda_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = coda_setattr, }; diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index ec5c8325b503..b91c01ebb688 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -279,11 +279,16 @@ static int configfs_getlink(struct dentry *dentry, char * path) } -static const char *configfs_follow_link(struct dentry *dentry, void **cookie) +static const char *configfs_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - unsigned long page = get_zeroed_page(GFP_KERNEL); + unsigned long page; int error; + if (!dentry) + return ERR_PTR(-ECHILD); + + page = get_zeroed_page(GFP_KERNEL); if (!page) return ERR_PTR(-ENOMEM); @@ -297,7 +302,7 @@ static const char *configfs_follow_link(struct dentry *dentry, void **cookie) } const struct inode_operations configfs_symlink_inode_operations = { - .follow_link = configfs_follow_link, + .get_link = configfs_get_link, .readlink = generic_readlink, .put_link = free_page_put_link, .setattr = configfs_setattr, diff --git a/fs/dcache.c b/fs/dcache.c index 5c33aeb0f68f..d27f0909d9f6 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1734,7 +1734,7 @@ static unsigned d_flags_for_inode(struct inode *inode) } if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) { - if (unlikely(inode->i_op->follow_link)) { + if (unlikely(inode->i_op->get_link)) { add_flags = DCACHE_SYMLINK_TYPE; goto type_determined; } diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index e2e47ba5d313..5a05559cb23d 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -674,10 +674,16 @@ out: return rc ? ERR_PTR(rc) : buf; } -static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie) +static const char *ecryptfs_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { size_t len; - char *buf = ecryptfs_readlink_lower(dentry, &len); + char *buf; + + if (!dentry) + return ERR_PTR(-ECHILD); + + buf = ecryptfs_readlink_lower(dentry, &len); if (IS_ERR(buf)) return buf; fsstack_copy_attr_atime(d_inode(dentry), @@ -1095,7 +1101,7 @@ out: const struct inode_operations ecryptfs_symlink_iops = { .readlink = generic_readlink, - .follow_link = ecryptfs_follow_link, + .get_link = ecryptfs_get_link, .put_link = kfree_put_link, .permission = ecryptfs_permission, .setattr = ecryptfs_setattr, diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index ae17179f3810..46905119a27c 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -22,7 +22,7 @@ const struct inode_operations ext2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = ext2_setattr, #ifdef CONFIG_EXT2_FS_XATTR @@ -35,7 +35,7 @@ const struct inode_operations ext2_symlink_inode_operations = { const struct inode_operations ext2_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ext2_setattr, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr, diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 0e6dc44c5ebf..3b4bfe2ebd75 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -23,17 +23,20 @@ #include "xattr.h" #ifdef CONFIG_EXT4_FS_ENCRYPTION -static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cookie) +static const char *ext4_encrypted_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { struct page *cpage = NULL; char *caddr, *paddr = NULL; struct ext4_str cstr, pstr; - struct inode *inode = d_inode(dentry); struct ext4_encrypted_symlink_data *sd; loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); int res; u32 plen, max_size = inode->i_sb->s_blocksize; + if (!dentry) + return ERR_PTR(-ECHILD); + res = ext4_get_encryption_info(inode); if (res) return ERR_PTR(res); @@ -87,7 +90,7 @@ errout: const struct inode_operations ext4_encrypted_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext4_encrypted_follow_link, + .get_link = ext4_encrypted_get_link, .put_link = kfree_put_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, @@ -99,7 +102,7 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = { const struct inode_operations ext4_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, @@ -110,7 +113,7 @@ const struct inode_operations ext4_symlink_inode_operations = { const struct inode_operations ext4_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 484df6850747..2a8d84b727ce 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -315,9 +315,10 @@ fail: return err; } -static const char *f2fs_follow_link(struct dentry *dentry, void **cookie) +static const char *f2fs_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - const char *link = page_follow_link_light(dentry, cookie); + const char *link = page_get_link(dentry, inode, cookie); if (!IS_ERR(link) && !*link) { /* this is broken symlink case */ page_put_link(NULL, *cookie); @@ -924,18 +925,21 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, } #ifdef CONFIG_F2FS_FS_ENCRYPTION -static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cookie) +static const char *f2fs_encrypted_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { struct page *cpage = NULL; char *caddr, *paddr = NULL; struct f2fs_str cstr; struct f2fs_str pstr = FSTR_INIT(NULL, 0); - struct inode *inode = d_inode(dentry); struct f2fs_encrypted_symlink_data *sd; loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1); u32 max_size = inode->i_sb->s_blocksize; int res; + if (!dentry) + return ERR_PTR(-ECHILD); + res = f2fs_get_encryption_info(inode); if (res) return ERR_PTR(res); @@ -994,7 +998,7 @@ errout: const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = f2fs_encrypted_follow_link, + .get_link = f2fs_encrypted_get_link, .put_link = kfree_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, @@ -1030,7 +1034,7 @@ const struct inode_operations f2fs_dir_inode_operations = { const struct inode_operations f2fs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = f2fs_follow_link, + .get_link = f2fs_get_link, .put_link = page_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 5e2e08712d3b..148e8ef7c541 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1365,14 +1365,17 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) return err; } -static const char *fuse_follow_link(struct dentry *dentry, void **cookie) +static const char *fuse_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct inode *inode = d_inode(dentry); struct fuse_conn *fc = get_fuse_conn(inode); FUSE_ARGS(args); char *link; ssize_t ret; + if (!dentry) + return ERR_PTR(-ECHILD); + link = (char *) __get_free_page(GFP_KERNEL); if (!link) return ERR_PTR(-ENOMEM); @@ -1909,7 +1912,7 @@ static const struct inode_operations fuse_common_inode_operations = { static const struct inode_operations fuse_symlink_inode_operations = { .setattr = fuse_setattr, - .follow_link = fuse_follow_link, + .get_link = fuse_get_link, .put_link = free_page_put_link, .readlink = generic_readlink, .getattr = fuse_getattr, diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 063fdfcf8275..1095056046cc 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1712,24 +1712,29 @@ static int gfs2_rename2(struct inode *odir, struct dentry *odentry, } /** - * gfs2_follow_link - Follow a symbolic link + * gfs2_get_link - Follow a symbolic link * @dentry: The dentry of the link - * @nd: Data that we pass to vfs_follow_link() + * @inode: The inode of the link + * @cookie: place to store the information for ->put_link() * * This can handle symlinks of any size. * * Returns: 0 on success or error code */ -static const char *gfs2_follow_link(struct dentry *dentry, void **cookie) +static const char *gfs2_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct gfs2_inode *ip = GFS2_I(d_inode(dentry)); + struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder i_gh; struct buffer_head *dibh; unsigned int size; char *buf; int error; + if (!dentry) + return ERR_PTR(-ECHILD); + gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh); error = gfs2_glock_nq(&i_gh); if (error) { @@ -2132,7 +2137,7 @@ const struct inode_operations gfs2_dir_iops = { const struct inode_operations gfs2_symlink_iops = { .readlink = generic_readlink, - .follow_link = gfs2_follow_link, + .get_link = gfs2_get_link, .put_link = kfree_put_link, .permission = gfs2_permission, .setattr = gfs2_setattr, diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 2ac99db3750e..6ce5309ecb7b 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -892,9 +892,13 @@ static const struct inode_operations hostfs_dir_iops = { .setattr = hostfs_setattr, }; -static const char *hostfs_follow_link(struct dentry *dentry, void **cookie) +static const char *hostfs_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - char *link = __getname(); + char *link; + if (!dentry) + return ERR_PTR(-ECHILD); + link = __getname(); if (link) { char *path = dentry_name(dentry); int err = -ENOMEM; @@ -922,7 +926,7 @@ static void hostfs_put_link(struct inode *unused, void *cookie) static const struct inode_operations hostfs_link_iops = { .readlink = generic_readlink, - .follow_link = hostfs_follow_link, + .get_link = hostfs_get_link, .put_link = hostfs_put_link, }; diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index 8ce2f240125b..2cabd649d4fb 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -14,7 +14,7 @@ const struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = jffs2_setattr, .setxattr = jffs2_setxattr, .getxattr = jffs2_getxattr, diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c index 5929e2363cb8..02113282772e 100644 --- a/fs/jfs/symlink.c +++ b/fs/jfs/symlink.c @@ -23,7 +23,7 @@ const struct inode_operations jfs_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, .getxattr = jfs_getxattr, @@ -33,7 +33,7 @@ const struct inode_operations jfs_fast_symlink_inode_operations = { const struct inode_operations jfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index db272528ab5b..ffae8579045d 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -112,10 +112,15 @@ static int kernfs_getlink(struct dentry *dentry, char *path) return error; } -static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie) +static const char *kernfs_iop_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { int error = -ENOMEM; - unsigned long page = get_zeroed_page(GFP_KERNEL); + unsigned long page; + + if (!dentry) + return ERR_PTR(-ECHILD); + page = get_zeroed_page(GFP_KERNEL); if (!page) return ERR_PTR(-ENOMEM); error = kernfs_getlink(dentry, (char *)page); @@ -132,7 +137,7 @@ const struct inode_operations kernfs_symlink_iops = { .getxattr = kernfs_iop_getxattr, .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, - .follow_link = kernfs_iop_follow_link, + .get_link = kernfs_iop_get_link, .put_link = free_page_put_link, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, diff --git a/fs/libfs.c b/fs/libfs.c index c7cbfb092e94..8dc37fc4b6df 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1092,14 +1092,15 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp, } EXPORT_SYMBOL(simple_nosetlease); -const char *simple_follow_link(struct dentry *dentry, void **cookie) +const char *simple_get_link(struct dentry *dentry, struct inode *inode, + void **cookie) { - return d_inode(dentry)->i_link; + return inode->i_link; } -EXPORT_SYMBOL(simple_follow_link); +EXPORT_SYMBOL(simple_get_link); const struct inode_operations simple_symlink_inode_operations = { - .follow_link = simple_follow_link, + .get_link = simple_get_link, .readlink = generic_readlink }; EXPORT_SYMBOL(simple_symlink_inode_operations); diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 67a23bfd7303..3cce709a8729 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -435,7 +435,7 @@ static const struct address_space_operations minix_aops = { static const struct inode_operations minix_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getattr = minix_getattr, }; diff --git a/fs/namei.c b/fs/namei.c index 2808958e6c67..1da3064311e2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -842,7 +842,7 @@ static inline void path_to_nameidata(const struct path *path, } /* - * Helper to directly jump to a known parsed path from ->follow_link, + * Helper to directly jump to a known parsed path from ->get_link, * caller must have taken a reference to path beforehand. */ void nd_jump_link(struct path *path) @@ -1005,10 +1005,18 @@ const char *get_link(struct nameidata *nd) res = inode->i_link; if (!res) { if (nd->flags & LOOKUP_RCU) { - if (unlikely(unlazy_walk(nd, NULL, 0))) - return ERR_PTR(-ECHILD); + res = inode->i_op->get_link(NULL, inode, + &last->cookie); + if (res == ERR_PTR(-ECHILD)) { + if (unlikely(unlazy_walk(nd, NULL, 0))) + return ERR_PTR(-ECHILD); + res = inode->i_op->get_link(dentry, inode, + &last->cookie); + } + } else { + res = inode->i_op->get_link(dentry, inode, + &last->cookie); } - res = inode->i_op->follow_link(dentry, &last->cookie); if (IS_ERR_OR_NULL(res)) { last->cookie = NULL; return res; @@ -4495,8 +4503,8 @@ EXPORT_SYMBOL(readlink_copy); /* * A helper for ->readlink(). This should be used *ONLY* for symlinks that - * have ->follow_link() touching nd only in nd_set_link(). Using (or not - * using) it for any given inode is up to filesystem. + * have ->get_link() not calling nd_jump_link(). Using (or not using) it + * for any given inode is up to filesystem. */ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) { @@ -4506,7 +4514,7 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) int res; if (!link) { - link = inode->i_op->follow_link(dentry, &cookie); + link = inode->i_op->get_link(dentry, inode, &cookie); if (IS_ERR(link)) return PTR_ERR(link); } @@ -4518,26 +4526,27 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) EXPORT_SYMBOL(generic_readlink); /* get the link contents into pagecache */ -static const char *page_getlink(struct dentry * dentry, void **cookie) +const char *page_get_link(struct dentry *dentry, struct inode *inode, + void **cookie) { char *kaddr; struct page *page; - struct address_space *mapping = dentry->d_inode->i_mapping; + struct address_space *mapping = inode->i_mapping; + + if (!dentry) + return ERR_PTR(-ECHILD); + page = read_mapping_page(mapping, 0, NULL); if (IS_ERR(page)) return (char*)page; *cookie = page; BUG_ON(mapping_gfp_mask(mapping) & __GFP_HIGHMEM); kaddr = page_address(page); - nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1); + nd_terminate_link(kaddr, inode->i_size, PAGE_SIZE - 1); return kaddr; } -const char *page_follow_link_light(struct dentry *dentry, void **cookie) -{ - return page_getlink(dentry, cookie); -} -EXPORT_SYMBOL(page_follow_link_light); +EXPORT_SYMBOL(page_get_link); void page_put_link(struct inode *unused, void *cookie) { @@ -4549,7 +4558,9 @@ EXPORT_SYMBOL(page_put_link); int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) { void *cookie = NULL; - int res = readlink_copy(buffer, buflen, page_getlink(dentry, &cookie)); + int res = readlink_copy(buffer, buflen, + page_get_link(dentry, d_inode(dentry), + &cookie)); if (cookie) page_put_link(NULL, cookie); return res; @@ -4600,7 +4611,7 @@ EXPORT_SYMBOL(page_symlink); const struct inode_operations page_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, }; EXPORT_SYMBOL(page_symlink_inode_operations); diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index bb856f7e05fd..3ab6cdbcde60 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -244,7 +244,7 @@ static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo) #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) static const struct inode_operations ncp_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = ncp_notify_change, }; diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index abd93bf015d6..8ade8a812607 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -42,12 +42,15 @@ error: return -EIO; } -static const char *nfs_follow_link(struct dentry *dentry, void **cookie) +static const char *nfs_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct inode *inode = d_inode(dentry); struct page *page; void *err; + if (!dentry) + return ERR_PTR(-ECHILD); + err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); if (err) return err; @@ -64,7 +67,7 @@ static const char *nfs_follow_link(struct dentry *dentry, void **cookie) */ const struct inode_operations nfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = nfs_follow_link, + .get_link = nfs_get_link, .put_link = page_put_link, .getattr = nfs_getattr, .setattr = nfs_setattr, diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 90b3ba960b9b..63dddb7d4b18 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -569,7 +569,7 @@ const struct inode_operations nilfs_special_inode_operations = { const struct inode_operations nilfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .permission = nilfs_permission, }; diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c index 66edce7ecfd7..b4e79bc720f7 100644 --- a/fs/ocfs2/symlink.c +++ b/fs/ocfs2/symlink.c @@ -88,7 +88,7 @@ const struct address_space_operations ocfs2_fast_symlink_aops = { const struct inode_operations ocfs2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr, diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 4060ffde8722..38a0b8b9f8b9 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -137,17 +137,21 @@ struct ovl_link_data { void *cookie; }; -static const char *ovl_follow_link(struct dentry *dentry, void **cookie) +static const char *ovl_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { struct dentry *realdentry; struct inode *realinode; struct ovl_link_data *data = NULL; const char *ret; + if (!dentry) + return ERR_PTR(-ECHILD); + realdentry = ovl_dentry_real(dentry); realinode = realdentry->d_inode; - if (WARN_ON(!realinode->i_op->follow_link)) + if (WARN_ON(!realinode->i_op->get_link)) return ERR_PTR(-EPERM); if (realinode->i_op->put_link) { @@ -157,7 +161,7 @@ static const char *ovl_follow_link(struct dentry *dentry, void **cookie) data->realdentry = realdentry; } - ret = realinode->i_op->follow_link(realdentry, cookie); + ret = realinode->i_op->get_link(realdentry, realinode, cookie); if (IS_ERR_OR_NULL(ret)) { kfree(data); return ret; @@ -378,7 +382,7 @@ static const struct inode_operations ovl_file_inode_operations = { static const struct inode_operations ovl_symlink_inode_operations = { .setattr = ovl_setattr, - .follow_link = ovl_follow_link, + .get_link = ovl_get_link, .put_link = ovl_put_link, .readlink = ovl_readlink, .getattr = ovl_getattr, diff --git a/fs/proc/base.c b/fs/proc/base.c index bd3e9e68125b..1a489e2b9768 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1564,12 +1564,15 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) return -ENOENT; } -static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_pid_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct inode *inode = d_inode(dentry); struct path path; int error = -EACCES; + if (!dentry) + return ERR_PTR(-ECHILD); + /* Are we allowed to snoop on the tasks file descriptors? */ if (!proc_fd_access_allowed(inode)) goto out; @@ -1630,7 +1633,7 @@ out: const struct inode_operations proc_pid_link_inode_operations = { .readlink = proc_pid_readlink, - .follow_link = proc_pid_follow_link, + .get_link = proc_pid_get_link, .setattr = proc_setattr, }; @@ -1895,7 +1898,7 @@ static const struct dentry_operations tid_map_files_dentry_operations = { .d_delete = pid_delete_dentry, }; -static int proc_map_files_get_link(struct dentry *dentry, struct path *path) +static int map_files_get_link(struct dentry *dentry, struct path *path) { unsigned long vm_start, vm_end; struct vm_area_struct *vma; @@ -1945,20 +1948,21 @@ struct map_files_info { * path to the file in question. */ static const char * -proc_map_files_follow_link(struct dentry *dentry, void **cookie) +proc_map_files_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { if (!capable(CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); - return proc_pid_follow_link(dentry, NULL); + return proc_pid_get_link(dentry, inode, NULL); } /* - * Identical to proc_pid_link_inode_operations except for follow_link() + * Identical to proc_pid_link_inode_operations except for get_link() */ static const struct inode_operations proc_map_files_link_inode_operations = { .readlink = proc_pid_readlink, - .follow_link = proc_map_files_follow_link, + .get_link = proc_map_files_get_link, .setattr = proc_setattr, }; @@ -1975,7 +1979,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry, return -ENOENT; ei = PROC_I(inode); - ei->op.proc_get_link = proc_map_files_get_link; + ei->op.proc_get_link = map_files_get_link; inode->i_op = &proc_map_files_link_inode_operations; inode->i_size = 64; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index bd95b9fdebb0..10360b268794 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -393,9 +393,10 @@ static const struct file_operations proc_reg_file_ops_no_compat = { }; #endif -static const char *proc_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct proc_dir_entry *pde = PDE(d_inode(dentry)); + struct proc_dir_entry *pde = PDE(inode); if (unlikely(!use_pde(pde))) return ERR_PTR(-EINVAL); *cookie = pde; @@ -409,7 +410,7 @@ static void proc_put_link(struct inode *unused, void *p) const struct inode_operations proc_link_inode_operations = { .readlink = generic_readlink, - .follow_link = proc_follow_link, + .get_link = proc_get_link, .put_link = proc_put_link, }; diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index f6e8354b8cea..63861c15e109 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -30,14 +30,17 @@ static const struct proc_ns_operations *ns_entries[] = { &mntns_operations, }; -static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_ns_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct inode *inode = d_inode(dentry); const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; struct task_struct *task; struct path ns_path; void *error = ERR_PTR(-EACCES); + if (!dentry) + return ERR_PTR(-ECHILD); + task = get_proc_task(inode); if (!task) return error; @@ -74,7 +77,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl static const struct inode_operations proc_ns_link_inode_operations = { .readlink = proc_ns_readlink, - .follow_link = proc_ns_follow_link, + .get_link = proc_ns_get_link, .setattr = proc_setattr, }; diff --git a/fs/proc/self.c b/fs/proc/self.c index 113b8d061fc0..9dd0ae6aefdb 100644 --- a/fs/proc/self.c +++ b/fs/proc/self.c @@ -18,12 +18,15 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static const char *proc_self_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_self_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct pid_namespace *ns = dentry->d_sb->s_fs_info; + struct pid_namespace *ns = inode->i_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); char *name; + if (!dentry) + return ERR_PTR(-ECHILD); if (!tgid) return ERR_PTR(-ENOENT); /* 11 for max length of signed int in decimal + NULL term */ @@ -36,7 +39,7 @@ static const char *proc_self_follow_link(struct dentry *dentry, void **cookie) static const struct inode_operations proc_self_inode_operations = { .readlink = proc_self_readlink, - .follow_link = proc_self_follow_link, + .get_link = proc_self_get_link, .put_link = kfree_put_link, }; diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c index 947b0f4fd0a1..50eef6f3e671 100644 --- a/fs/proc/thread_self.c +++ b/fs/proc/thread_self.c @@ -19,13 +19,16 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static const char *proc_thread_self_follow_link(struct dentry *dentry, void **cookie) +static const char *proc_thread_self_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { - struct pid_namespace *ns = dentry->d_sb->s_fs_info; + struct pid_namespace *ns = inode->i_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); pid_t pid = task_pid_nr_ns(current, ns); char *name; + if (!dentry) + return ERR_PTR(-ECHILD); if (!pid) return ERR_PTR(-ENOENT); name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); @@ -37,7 +40,7 @@ static const char *proc_thread_self_follow_link(struct dentry *dentry, void **co static const struct inode_operations proc_thread_self_inode_operations = { .readlink = proc_thread_self_readlink, - .follow_link = proc_thread_self_follow_link, + .get_link = proc_thread_self_get_link, .put_link = kfree_put_link, }; diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index 4fc2326fac03..ecbf11e961ab 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -1665,7 +1665,7 @@ const struct inode_operations reiserfs_dir_inode_operations = { */ const struct inode_operations reiserfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .setattr = reiserfs_setattr, .setxattr = reiserfs_setxattr, diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c index 12806dffb345..7c635a5da783 100644 --- a/fs/squashfs/symlink.c +++ b/fs/squashfs/symlink.c @@ -119,7 +119,7 @@ const struct address_space_operations squashfs_symlink_aops = { const struct inode_operations squashfs_symlink_inode_ops = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getxattr = generic_getxattr, .listxattr = squashfs_listxattr diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index ef8bcdb80aca..80a40bcb721c 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -146,7 +146,7 @@ static inline void write3byte(struct sysv_sb_info *sbi, static const struct inode_operations sysv_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = page_follow_link_light, + .get_link = page_get_link, .put_link = page_put_link, .getattr = sysv_getattr, }; diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 0edc12856147..eff62801acbf 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1608,7 +1608,7 @@ const struct inode_operations ubifs_file_inode_operations = { const struct inode_operations ubifs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .setxattr = ubifs_setxattr, diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 245268a0cdf0..f638fd58b5b3 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -414,13 +414,17 @@ xfs_vn_rename( * uio is kmalloced for this reason... */ STATIC const char * -xfs_vn_follow_link( +xfs_vn_get_link( struct dentry *dentry, + struct inode *inode, void **cookie) { char *link; int error = -ENOMEM; + if (!dentry) + return ERR_PTR(-ECHILD); + link = kmalloc(MAXPATHLEN+1, GFP_KERNEL); if (!link) goto out_err; @@ -1172,7 +1176,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = { static const struct inode_operations xfs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = xfs_vn_follow_link, + .get_link = xfs_vn_get_link, .put_link = kfree_put_link, .getattr = xfs_vn_getattr, .setattr = xfs_vn_setattr, diff --git a/include/linux/fs.h b/include/linux/fs.h index dfeda44b9ba4..d2fdf09a4407 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1633,7 +1633,7 @@ struct file_operations { struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); - const char * (*follow_link) (struct dentry *, void **); + const char * (*get_link) (struct dentry *, struct inode *, void **); int (*permission) (struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); @@ -2736,7 +2736,7 @@ 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 const char *page_follow_link_light(struct dentry *, void **); +extern const char *page_get_link(struct dentry *, struct inode *, void **); extern void page_put_link(struct inode *, void *); extern int __page_symlink(struct inode *inode, const char *symname, int len, int nofs); @@ -2754,7 +2754,7 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes); void inode_sub_bytes(struct inode *inode, loff_t bytes); loff_t inode_get_bytes(struct inode *inode); void inode_set_bytes(struct inode *inode, loff_t bytes); -const char *simple_follow_link(struct dentry *, void **); +const char *simple_get_link(struct dentry *, struct inode *, void **); extern const struct inode_operations simple_symlink_inode_operations; extern int iterate_dir(struct file *, struct dir_context *); diff --git a/mm/shmem.c b/mm/shmem.c index 64bf5acb49fe..684dbc32e233 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2496,10 +2496,14 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return 0; } -static const char *shmem_follow_link(struct dentry *dentry, void **cookie) +static const char *shmem_get_link(struct dentry *dentry, + struct inode *inode, void **cookie) { struct page *page = NULL; - int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL); + int error; + if (!dentry) + return ERR_PTR(-ECHILD); + error = shmem_getpage(inode, 0, &page, SGP_READ, NULL); if (error) return ERR_PTR(error); unlock_page(page); @@ -2656,7 +2660,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) static const struct inode_operations shmem_short_symlink_operations = { .readlink = generic_readlink, - .follow_link = simple_follow_link, + .get_link = simple_get_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, .getxattr = shmem_getxattr, @@ -2667,7 +2671,7 @@ static const struct inode_operations shmem_short_symlink_operations = { static const struct inode_operations shmem_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = shmem_follow_link, + .get_link = shmem_get_link, .put_link = shmem_put_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, -- cgit v1.2.3 From 6a6c99049635473b64c384135a6906a10df2c916 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 17 Nov 2015 10:54:32 -0500 Subject: teach shmem_get_link() to work in RCU mode Signed-off-by: Al Viro --- mm/shmem.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'mm/shmem.c') diff --git a/mm/shmem.c b/mm/shmem.c index 684dbc32e233..0605716aee06 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2501,12 +2501,20 @@ static const char *shmem_get_link(struct dentry *dentry, { struct page *page = NULL; int error; - if (!dentry) - return ERR_PTR(-ECHILD); - error = shmem_getpage(inode, 0, &page, SGP_READ, NULL); - if (error) - return ERR_PTR(error); - unlock_page(page); + if (!dentry) { + page = find_get_page(inode->i_mapping, 0); + if (!page) + return ERR_PTR(-ECHILD); + if (!PageUptodate(page)) { + put_page(page); + return ERR_PTR(-ECHILD); + } + } else { + error = shmem_getpage(inode, 0, &page, SGP_READ, NULL); + if (error) + return ERR_PTR(error); + unlock_page(page); + } *cookie = page; return page_address(page); } -- cgit v1.2.3 From fceef393a538134f03b778c5d2519e670269342f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 29 Dec 2015 15:58:39 -0500 Subject: switch ->get_link() to delayed_call, kill ->put_link() Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 2 - Documentation/filesystems/porting | 6 +++ Documentation/filesystems/vfs.txt | 21 +++++---- drivers/staging/lustre/lustre/llite/symlink.c | 18 ++++---- fs/9p/vfs_inode.c | 9 ++-- fs/9p/vfs_inode_dotl.c | 9 ++-- fs/affs/symlink.c | 1 - fs/autofs4/symlink.c | 3 +- fs/btrfs/inode.c | 1 - fs/cifs/cifsfs.c | 1 - fs/cifs/cifsfs.h | 3 +- fs/cifs/link.c | 6 ++- fs/coda/cnode.c | 1 - fs/configfs/symlink.c | 17 ++++---- fs/ecryptfs/inode.c | 7 +-- fs/ext2/symlink.c | 1 - fs/ext4/symlink.c | 8 ++-- fs/f2fs/namei.c | 16 ++++--- fs/fuse/dir.c | 6 +-- fs/gfs2/inode.c | 8 ++-- fs/hostfs/hostfs_kern.c | 16 +++---- fs/jfs/symlink.c | 1 - fs/kernfs/symlink.c | 19 ++++---- fs/libfs.c | 9 ++-- fs/minix/inode.c | 1 - fs/namei.c | 63 +++++++++++---------------- fs/ncpfs/inode.c | 1 - fs/nfs/symlink.c | 6 +-- fs/nilfs2/namei.c | 1 - fs/ocfs2/symlink.c | 1 - fs/overlayfs/inode.c | 45 ++----------------- fs/proc/base.c | 8 ++-- fs/proc/inode.c | 16 +++---- fs/proc/namespaces.c | 3 +- fs/proc/self.c | 7 +-- fs/proc/thread_self.c | 7 +-- fs/reiserfs/namei.c | 1 - fs/squashfs/symlink.c | 1 - fs/sysv/inode.c | 1 - fs/xfs/xfs_iops.c | 6 +-- include/linux/delayed_call.h | 34 +++++++++++++++ include/linux/fs.h | 14 +++--- mm/shmem.c | 19 ++++---- 43 files changed, 206 insertions(+), 218 deletions(-) create mode 100644 include/linux/delayed_call.h (limited to 'mm/shmem.c') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 4fba54b9fcec..619af9bfdcb3 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -51,7 +51,6 @@ prototypes: struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); const char *(*get_link) (struct dentry *, struct inode *, void **); - void (*put_link) (struct inode *, void *); void (*truncate) (struct inode *); int (*permission) (struct inode *, int, unsigned int); int (*get_acl)(struct inode *, int); @@ -84,7 +83,6 @@ rename: yes (all) (see below) rename2: yes (all) (see below) readlink: no get_link: no -put_link: no setattr: yes permission: no (may not block if called in rcu-walk mode) get_acl: no diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index cf92a8c55594..0f88e6020487 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -515,3 +515,9 @@ in your dentry operations instead. * ->get_link() gets inode as a separate argument * ->get_link() may be called in RCU mode - in that case NULL dentry is passed +-- +[mandatory] + ->get_link() gets struct delayed_call *done now, and should do + set_delayed_call() where it used to set *cookie. + ->put_link() is gone - just give the destructor to set_delayed_call() + in ->get_link(). diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 8c6f07ad373a..b02a7d598258 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); - const char *(*follow_link) (struct dentry *, void **); - void (*put_link) (struct inode *, void *); + const char *(*get_link) (struct dentry *, struct inode *, + struct delayed_call *); int (*permission) (struct inode *, int); int (*get_acl)(struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); @@ -434,20 +434,19 @@ otherwise noted. readlink: called by the readlink(2) system call. Only required if you want to support reading symbolic links - follow_link: called by the VFS to follow a symbolic link to the + get_link: called by the VFS to follow a symbolic link to the inode it points to. Only required if you want to support symbolic links. This method returns the symlink body to traverse (and possibly resets the current position with nd_jump_link()). If the body won't go away until the inode is gone, nothing else is needed; if it needs to be otherwise - pinned, the data needed to release whatever we'd grabbed - is to be stored in void * variable passed by address to - follow_link() instance. - - put_link: called by the VFS to release resources allocated by - follow_link(). The cookie stored by follow_link() is passed - to this method as the last parameter; only called when - cookie isn't NULL. + pinned, arrange for its release by having get_link(..., ..., done) + do set_delayed_call(done, destructor, argument). + In that case destructor(argument) will be called once VFS is + done with the body you've returned. + May be called in RCU mode; that is indicated by NULL dentry + argument. If request can't be handled without leaving RCU mode, + have it return ERR_PTR(-ECHILD). permission: called by the VFS to check for access rights on a POSIX-like filesystem. diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c index 153fdf908328..e489a3271f06 100644 --- a/drivers/staging/lustre/lustre/llite/symlink.c +++ b/drivers/staging/lustre/lustre/llite/symlink.c @@ -118,8 +118,14 @@ failed: return rc; } +static void ll_put_link(void *p) +{ + ptlrpc_req_finished(p); +} + static const char *ll_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct ptlrpc_request *request = NULL; int rc; @@ -137,22 +143,16 @@ static const char *ll_get_link(struct dentry *dentry, } /* symname may contain a pointer to the request message buffer, - * we delay request releasing until ll_put_link then. + * we delay request releasing then. */ - *cookie = request; + set_delayed_call(done, ll_put_link, request); return symname; } -static void ll_put_link(struct inode *unused, void *cookie) -{ - ptlrpc_req_finished(cookie); -} - struct inode_operations ll_fast_symlink_inode_operations = { .readlink = generic_readlink, .setattr = ll_setattr, .get_link = ll_get_link, - .put_link = ll_put_link, .getattr = ll_getattr, .permission = ll_inode_permission, .setxattr = ll_setxattr, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 8ba5a897fc0a..f928f8702f4c 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1226,11 +1226,12 @@ ino_t v9fs_qid2ino(struct p9_qid *qid) * v9fs_vfs_get_link - follow a symlink path * @dentry: dentry for symlink * @inode: inode for symlink - * @cookie: place to pass the data to put_link() + * @done: delayed call for when we are done with the return value */ static const char *v9fs_vfs_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct v9fs_session_info *v9ses; struct p9_fid *fid; @@ -1266,7 +1267,8 @@ static const char *v9fs_vfs_get_link(struct dentry *dentry, p9stat_free(st); kfree(st); - return *cookie = res; + set_delayed_call(done, kfree_link, res); + return res; } /** @@ -1460,7 +1462,6 @@ static const struct inode_operations v9fs_file_inode_operations = { static const struct inode_operations v9fs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = v9fs_vfs_get_link, - .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr, .setattr = v9fs_vfs_setattr, }; diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 0cc105d804dd..a34702c998f5 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -902,12 +902,13 @@ error: * v9fs_vfs_get_link_dotl - follow a symlink path * @dentry: dentry for symlink * @inode: inode for symlink - * @cookie: place to pass the data to put_link() + * @done: destructor for return value */ static const char * v9fs_vfs_get_link_dotl(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct p9_fid *fid; char *target; @@ -924,7 +925,8 @@ v9fs_vfs_get_link_dotl(struct dentry *dentry, retval = p9_client_readlink(fid, &target); if (retval) return ERR_PTR(retval); - return *cookie = target; + set_delayed_call(done, kfree_link, target); + return target; } int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode) @@ -991,7 +993,6 @@ const struct inode_operations v9fs_file_inode_operations_dotl = { const struct inode_operations v9fs_symlink_inode_operations_dotl = { .readlink = generic_readlink, .get_link = v9fs_vfs_get_link_dotl, - .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, .setxattr = generic_setxattr, diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index 39d1194445e1..69b03dbb792f 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -72,6 +72,5 @@ const struct address_space_operations affs_symlink_aops = { const struct inode_operations affs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = affs_notify_change, }; diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c index 39e6f0bdf8e3..84e037d1d129 100644 --- a/fs/autofs4/symlink.c +++ b/fs/autofs4/symlink.c @@ -13,7 +13,8 @@ #include "autofs_i.h" static const char *autofs4_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct autofs_sb_info *sbi; struct autofs_info *ino; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 3d4aa69f1e0c..1a41a65fd2ff 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10097,7 +10097,6 @@ static const struct inode_operations btrfs_special_inode_operations = { static const struct inode_operations btrfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getattr = btrfs_getattr, .setattr = btrfs_setattr, .permission = btrfs_permission, diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 4593f41678ef..90e4e2b398b6 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -901,7 +901,6 @@ const struct inode_operations cifs_file_inode_ops = { const struct inode_operations cifs_symlink_inode_ops = { .readlink = generic_readlink, .get_link = cifs_get_link, - .put_link = kfree_put_link, .permission = cifs_permission, /* BB add the following two eventually */ /* revalidate: cifs_revalidate, diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 6886328cf3c4..26a1187d4323 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -120,7 +120,8 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path); #endif /* Functions related to symlinks */ -extern const char *cifs_get_link(struct dentry *, struct inode *, void **); +extern const char *cifs_get_link(struct dentry *, struct inode *, + struct delayed_call *); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname); extern int cifs_removexattr(struct dentry *, const char *); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 6f2439b508b5..062c2375549a 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -627,7 +627,8 @@ cifs_hl_exit: } const char * -cifs_get_link(struct dentry *direntry, struct inode *inode, void **cookie) +cifs_get_link(struct dentry *direntry, struct inode *inode, + struct delayed_call *done) { int rc = -ENOMEM; unsigned int xid; @@ -680,7 +681,8 @@ cifs_get_link(struct dentry *direntry, struct inode *inode, void **cookie) kfree(target_path); return ERR_PTR(rc); } - return *cookie = target_path; + set_delayed_call(done, kfree_link, target_path); + return target_path; } int diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index f18139c7690a..1bfb7ba4e85e 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -19,7 +19,6 @@ static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2) static const struct inode_operations coda_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = coda_setattr, }; diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index e9de962e518d..db6d69289608 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -280,31 +280,32 @@ static int configfs_getlink(struct dentry *dentry, char * path) } static const char *configfs_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { - char *page; + char *body; int error; if (!dentry) return ERR_PTR(-ECHILD); - page = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!page) + body = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!body) return ERR_PTR(-ENOMEM); - error = configfs_getlink(dentry, page); + error = configfs_getlink(dentry, body); if (!error) { - return *cookie = page; + set_delayed_call(done, kfree_link, body); + return body; } - kfree(page); + kfree(body); return ERR_PTR(error); } const struct inode_operations configfs_symlink_inode_operations = { .get_link = configfs_get_link, .readlink = generic_readlink, - .put_link = kfree_put_link, .setattr = configfs_setattr, }; diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 5a05559cb23d..a4dddc61594c 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -675,7 +675,8 @@ out: } static const char *ecryptfs_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { size_t len; char *buf; @@ -689,7 +690,8 @@ static const char *ecryptfs_get_link(struct dentry *dentry, fsstack_copy_attr_atime(d_inode(dentry), d_inode(ecryptfs_dentry_to_lower(dentry))); buf[len] = '\0'; - return *cookie = buf; + set_delayed_call(done, kfree_link, buf); + return buf; } /** @@ -1102,7 +1104,6 @@ out: const struct inode_operations ecryptfs_symlink_iops = { .readlink = generic_readlink, .get_link = ecryptfs_get_link, - .put_link = kfree_put_link, .permission = ecryptfs_permission, .setattr = ecryptfs_setattr, .getattr = ecryptfs_getattr_link, diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 46905119a27c..3495d8ae4b33 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -23,7 +23,6 @@ const struct inode_operations ext2_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = ext2_setattr, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr, diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 3b4bfe2ebd75..2281ac27b213 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -24,7 +24,8 @@ #ifdef CONFIG_EXT4_FS_ENCRYPTION static const char *ext4_encrypted_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct page *cpage = NULL; char *caddr, *paddr = NULL; @@ -80,7 +81,8 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry, paddr[res] = '\0'; if (cpage) page_cache_release(cpage); - return *cookie = paddr; + set_delayed_call(done, kfree_link, paddr); + return paddr; errout: if (cpage) page_cache_release(cpage); @@ -91,7 +93,6 @@ errout: const struct inode_operations ext4_encrypted_symlink_inode_operations = { .readlink = generic_readlink, .get_link = ext4_encrypted_get_link, - .put_link = kfree_put_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, @@ -103,7 +104,6 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = { const struct inode_operations ext4_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 2a8d84b727ce..e7587fce1b80 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -316,12 +316,14 @@ fail: } static const char *f2fs_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { - const char *link = page_get_link(dentry, inode, cookie); + const char *link = page_get_link(dentry, inode, done); if (!IS_ERR(link) && !*link) { /* this is broken symlink case */ - page_put_link(NULL, *cookie); + do_delayed_call(done); + clear_delayed_call(done); link = ERR_PTR(-ENOENT); } return link; @@ -926,7 +928,8 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry, #ifdef CONFIG_F2FS_FS_ENCRYPTION static const char *f2fs_encrypted_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct page *cpage = NULL; char *caddr, *paddr = NULL; @@ -988,7 +991,8 @@ static const char *f2fs_encrypted_get_link(struct dentry *dentry, paddr[res] = '\0'; page_cache_release(cpage); - return *cookie = paddr; + set_delayed_call(done, kfree_link, paddr); + return paddr; errout: kfree(cstr.name); f2fs_fname_crypto_free_buffer(&pstr); @@ -999,7 +1003,6 @@ errout: const struct inode_operations f2fs_encrypted_symlink_inode_operations = { .readlink = generic_readlink, .get_link = f2fs_encrypted_get_link, - .put_link = kfree_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, .setxattr = generic_setxattr, @@ -1035,7 +1038,6 @@ const struct inode_operations f2fs_dir_inode_operations = { const struct inode_operations f2fs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = f2fs_get_link, - .put_link = page_put_link, .getattr = f2fs_getattr, .setattr = f2fs_setattr, #ifdef CONFIG_F2FS_FS_XATTR diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index def0a4d082bc..712601f299b8 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1366,7 +1366,8 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) } static const char *fuse_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct fuse_conn *fc = get_fuse_conn(inode); FUSE_ARGS(args); @@ -1392,7 +1393,7 @@ static const char *fuse_get_link(struct dentry *dentry, link = ERR_PTR(ret); } else { link[ret] = '\0'; - *cookie = link; + set_delayed_call(done, kfree_link, link); } fuse_invalidate_atime(inode); return link; @@ -1913,7 +1914,6 @@ static const struct inode_operations fuse_common_inode_operations = { static const struct inode_operations fuse_symlink_inode_operations = { .setattr = fuse_setattr, .get_link = fuse_get_link, - .put_link = kfree_put_link, .readlink = generic_readlink, .getattr = fuse_getattr, .setxattr = fuse_setxattr, diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 1095056046cc..1bae189f3245 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1715,7 +1715,7 @@ static int gfs2_rename2(struct inode *odir, struct dentry *odentry, * gfs2_get_link - Follow a symbolic link * @dentry: The dentry of the link * @inode: The inode of the link - * @cookie: place to store the information for ->put_link() + * @done: destructor for return value * * This can handle symlinks of any size. * @@ -1723,7 +1723,8 @@ static int gfs2_rename2(struct inode *odir, struct dentry *odentry, */ static const char *gfs2_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder i_gh; @@ -1764,7 +1765,7 @@ static const char *gfs2_get_link(struct dentry *dentry, out: gfs2_glock_dq_uninit(&i_gh); if (!IS_ERR(buf)) - *cookie = buf; + set_delayed_call(done, kfree_link, buf); return buf; } @@ -2138,7 +2139,6 @@ const struct inode_operations gfs2_dir_iops = { const struct inode_operations gfs2_symlink_iops = { .readlink = generic_readlink, .get_link = gfs2_get_link, - .put_link = kfree_put_link, .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 6ce5309ecb7b..7db524cc85b6 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -893,12 +893,13 @@ static const struct inode_operations hostfs_dir_iops = { }; static const char *hostfs_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { char *link; if (!dentry) return ERR_PTR(-ECHILD); - link = __getname(); + link = kmalloc(PATH_MAX, GFP_KERNEL); if (link) { char *path = dentry_name(dentry); int err = -ENOMEM; @@ -909,25 +910,20 @@ static const char *hostfs_get_link(struct dentry *dentry, __putname(path); } if (err < 0) { - __putname(link); + kfree(link); return ERR_PTR(err); } } else { return ERR_PTR(-ENOMEM); } - return *cookie = link; -} - -static void hostfs_put_link(struct inode *unused, void *cookie) -{ - __putname(cookie); + set_delayed_call(done, kfree_link, link); + return link; } static const struct inode_operations hostfs_link_iops = { .readlink = generic_readlink, .get_link = hostfs_get_link, - .put_link = hostfs_put_link, }; static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c index 02113282772e..f8db4fde0b0b 100644 --- a/fs/jfs/symlink.c +++ b/fs/jfs/symlink.c @@ -34,7 +34,6 @@ const struct inode_operations jfs_fast_symlink_inode_operations = { const struct inode_operations jfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, .getxattr = jfs_getxattr, diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index f9efdaeda7b0..117b8b3416f9 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -113,22 +113,24 @@ static int kernfs_getlink(struct dentry *dentry, char *path) } static const char *kernfs_iop_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { - int error = -ENOMEM; - char *page; + char *body; + int error; if (!dentry) return ERR_PTR(-ECHILD); - page = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!page) + body = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!body) return ERR_PTR(-ENOMEM); - error = kernfs_getlink(dentry, page); + error = kernfs_getlink(dentry, body); if (unlikely(error < 0)) { - kfree(page); + kfree(body); return ERR_PTR(error); } - return *cookie = page; + set_delayed_call(done, kfree_link, body); + return body; } const struct inode_operations kernfs_symlink_iops = { @@ -138,7 +140,6 @@ const struct inode_operations kernfs_symlink_iops = { .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, .get_link = kernfs_iop_get_link, - .put_link = kfree_put_link, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, .permission = kernfs_iop_permission, diff --git a/fs/libfs.c b/fs/libfs.c index fec7ab0632dc..01491299f348 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1019,11 +1019,12 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync) } EXPORT_SYMBOL(noop_fsync); -void kfree_put_link(struct inode *unused, void *cookie) +/* Because kfree isn't assignment-compatible with void(void*) ;-/ */ +void kfree_link(void *p) { - kfree(cookie); + kfree(p); } -EXPORT_SYMBOL(kfree_put_link); +EXPORT_SYMBOL(kfree_link); /* * nop .set_page_dirty method so that people can use .page_mkwrite on @@ -1087,7 +1088,7 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp, EXPORT_SYMBOL(simple_nosetlease); const char *simple_get_link(struct dentry *dentry, struct inode *inode, - void **cookie) + struct delayed_call *done) { return inode->i_link; } diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 3cce709a8729..cb1789ca1ee6 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -436,7 +436,6 @@ static const struct address_space_operations minix_aops = { static const struct inode_operations minix_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getattr = minix_getattr, }; diff --git a/fs/namei.c b/fs/namei.c index 8f517888c3e1..3c909aebef70 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -505,13 +505,13 @@ struct nameidata { int total_link_count; struct saved { struct path link; - void *cookie; + struct delayed_call done; const char *name; - struct inode *inode; unsigned seq; } *stack, internal[EMBEDDED_LEVELS]; struct filename *name; struct nameidata *saved; + struct inode *link_inode; unsigned root_seq; int dfd; }; @@ -592,11 +592,8 @@ static void drop_links(struct nameidata *nd) int i = nd->depth; while (i--) { struct saved *last = nd->stack + i; - struct inode *inode = last->inode; - if (last->cookie && inode->i_op->put_link) { - inode->i_op->put_link(inode, last->cookie); - last->cookie = NULL; - } + do_delayed_call(&last->done); + clear_delayed_call(&last->done); } } @@ -858,9 +855,7 @@ void nd_jump_link(struct path *path) static inline void put_link(struct nameidata *nd) { struct saved *last = nd->stack + --nd->depth; - struct inode *inode = last->inode; - if (last->cookie && inode->i_op->put_link) - inode->i_op->put_link(inode, last->cookie); + do_delayed_call(&last->done); if (!(nd->flags & LOOKUP_RCU)) path_put(&last->link); } @@ -892,7 +887,7 @@ static inline int may_follow_link(struct nameidata *nd) return 0; /* Allowed if owner and follower match. */ - inode = nd->stack[0].inode; + inode = nd->link_inode; if (uid_eq(current_cred()->fsuid, inode->i_uid)) return 0; @@ -983,7 +978,7 @@ const char *get_link(struct nameidata *nd) { struct saved *last = nd->stack + nd->depth - 1; struct dentry *dentry = last->link.dentry; - struct inode *inode = last->inode; + struct inode *inode = nd->link_inode; int error; const char *res; @@ -1004,23 +999,21 @@ const char *get_link(struct nameidata *nd) nd->last_type = LAST_BIND; res = inode->i_link; if (!res) { + const char * (*get)(struct dentry *, struct inode *, + struct delayed_call *); + get = inode->i_op->get_link; if (nd->flags & LOOKUP_RCU) { - res = inode->i_op->get_link(NULL, inode, - &last->cookie); + res = get(NULL, inode, &last->done); if (res == ERR_PTR(-ECHILD)) { if (unlikely(unlazy_walk(nd, NULL, 0))) return ERR_PTR(-ECHILD); - res = inode->i_op->get_link(dentry, inode, - &last->cookie); + res = get(dentry, inode, &last->done); } } else { - res = inode->i_op->get_link(dentry, inode, - &last->cookie); + res = get(dentry, inode, &last->done); } - if (IS_ERR_OR_NULL(res)) { - last->cookie = NULL; + if (IS_ERR_OR_NULL(res)) return res; - } } if (*res == '/') { if (nd->flags & LOOKUP_RCU) { @@ -1699,8 +1692,8 @@ static int pick_link(struct nameidata *nd, struct path *link, last = nd->stack + nd->depth++; last->link = *link; - last->cookie = NULL; - last->inode = inode; + clear_delayed_call(&last->done); + nd->link_inode = inode; last->seq = seq; return 1; } @@ -4508,26 +4501,25 @@ EXPORT_SYMBOL(readlink_copy); */ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - void *cookie; + DEFINE_DELAYED_CALL(done); struct inode *inode = d_inode(dentry); const char *link = inode->i_link; int res; if (!link) { - link = inode->i_op->get_link(dentry, inode, &cookie); + link = inode->i_op->get_link(dentry, inode, &done); if (IS_ERR(link)) return PTR_ERR(link); } res = readlink_copy(buffer, buflen, link); - if (inode->i_op->put_link) - inode->i_op->put_link(inode, cookie); + do_delayed_call(&done); return res; } EXPORT_SYMBOL(generic_readlink); /* get the link contents into pagecache */ const char *page_get_link(struct dentry *dentry, struct inode *inode, - void **cookie) + struct delayed_call *callback) { char *kaddr; struct page *page; @@ -4546,7 +4538,7 @@ const char *page_get_link(struct dentry *dentry, struct inode *inode, if (IS_ERR(page)) return (char*)page; } - *cookie = page; + set_delayed_call(callback, page_put_link, page); BUG_ON(mapping_gfp_mask(mapping) & __GFP_HIGHMEM); kaddr = page_address(page); nd_terminate_link(kaddr, inode->i_size, PAGE_SIZE - 1); @@ -4555,21 +4547,19 @@ const char *page_get_link(struct dentry *dentry, struct inode *inode, EXPORT_SYMBOL(page_get_link); -void page_put_link(struct inode *unused, void *cookie) +void page_put_link(void *arg) { - struct page *page = cookie; - page_cache_release(page); + put_page(arg); } EXPORT_SYMBOL(page_put_link); int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - void *cookie = NULL; + DEFINE_DELAYED_CALL(done); int res = readlink_copy(buffer, buflen, page_get_link(dentry, d_inode(dentry), - &cookie)); - if (cookie) - page_put_link(NULL, cookie); + &done)); + do_delayed_call(&done); return res; } EXPORT_SYMBOL(page_readlink); @@ -4619,6 +4609,5 @@ EXPORT_SYMBOL(page_symlink); const struct inode_operations page_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, }; EXPORT_SYMBOL(page_symlink_inode_operations); diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 3ab6cdbcde60..ce1eb3f9dfe8 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -245,7 +245,6 @@ static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo) static const struct inode_operations ncp_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = ncp_notify_change, }; #endif diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 95c69af7e4d0..4fe3eead3868 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -43,7 +43,8 @@ error: } static const char *nfs_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct page *page; void *err; @@ -68,7 +69,7 @@ static const char *nfs_get_link(struct dentry *dentry, if (IS_ERR(page)) return ERR_CAST(page); } - *cookie = page; + set_delayed_call(done, page_put_link, page); return page_address(page); } @@ -78,7 +79,6 @@ static const char *nfs_get_link(struct dentry *dentry, const struct inode_operations nfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = nfs_get_link, - .put_link = page_put_link, .getattr = nfs_getattr, .setattr = nfs_setattr, }; diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 63dddb7d4b18..7ccdb961eea9 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -570,7 +570,6 @@ const struct inode_operations nilfs_special_inode_operations = { const struct inode_operations nilfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .permission = nilfs_permission, }; diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c index b4e79bc720f7..6c2a3e3c521c 100644 --- a/fs/ocfs2/symlink.c +++ b/fs/ocfs2/symlink.c @@ -89,7 +89,6 @@ const struct address_space_operations ocfs2_fast_symlink_aops = { const struct inode_operations ocfs2_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr, .setxattr = generic_setxattr, diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 38a0b8b9f8b9..964a60fa7afc 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -131,19 +131,12 @@ out_dput: return err; } - -struct ovl_link_data { - struct dentry *realdentry; - void *cookie; -}; - static const char *ovl_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct dentry *realdentry; struct inode *realinode; - struct ovl_link_data *data = NULL; - const char *ret; if (!dentry) return ERR_PTR(-ECHILD); @@ -154,38 +147,7 @@ static const char *ovl_get_link(struct dentry *dentry, if (WARN_ON(!realinode->i_op->get_link)) return ERR_PTR(-EPERM); - if (realinode->i_op->put_link) { - data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); - data->realdentry = realdentry; - } - - ret = realinode->i_op->get_link(realdentry, realinode, cookie); - if (IS_ERR_OR_NULL(ret)) { - kfree(data); - return ret; - } - - if (data) - data->cookie = *cookie; - - *cookie = data; - - return ret; -} - -static void ovl_put_link(struct inode *unused, void *c) -{ - struct inode *realinode; - struct ovl_link_data *data = c; - - if (!data) - return; - - realinode = data->realdentry->d_inode; - realinode->i_op->put_link(realinode, data->cookie); - kfree(data); + return realinode->i_op->get_link(realdentry, realinode, done); } static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) @@ -383,7 +345,6 @@ static const struct inode_operations ovl_file_inode_operations = { static const struct inode_operations ovl_symlink_inode_operations = { .setattr = ovl_setattr, .get_link = ovl_get_link, - .put_link = ovl_put_link, .readlink = ovl_readlink, .getattr = ovl_getattr, .setxattr = ovl_setxattr, diff --git a/fs/proc/base.c b/fs/proc/base.c index 1a489e2b9768..71660bb9e9f7 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1565,7 +1565,8 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) } static const char *proc_pid_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct path path; int error = -EACCES; @@ -1949,12 +1950,13 @@ struct map_files_info { */ static const char * proc_map_files_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { if (!capable(CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); - return proc_pid_get_link(dentry, inode, NULL); + return proc_pid_get_link(dentry, inode, done); } /* diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 10360b268794..d0e9b9b6223e 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -393,25 +393,25 @@ static const struct file_operations proc_reg_file_ops_no_compat = { }; #endif +static void proc_put_link(void *p) +{ + unuse_pde(p); +} + static const char *proc_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct proc_dir_entry *pde = PDE(inode); if (unlikely(!use_pde(pde))) return ERR_PTR(-EINVAL); - *cookie = pde; + set_delayed_call(done, proc_put_link, pde); return pde->data; } -static void proc_put_link(struct inode *unused, void *p) -{ - unuse_pde(p); -} - const struct inode_operations proc_link_inode_operations = { .readlink = generic_readlink, .get_link = proc_get_link, - .put_link = proc_put_link, }; struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 63861c15e109..1dece8781f91 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -31,7 +31,8 @@ static const struct proc_ns_operations *ns_entries[] = { }; static const char *proc_ns_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; struct task_struct *task; diff --git a/fs/proc/self.c b/fs/proc/self.c index 7a8b19ead3b6..67e8db442cf0 100644 --- a/fs/proc/self.c +++ b/fs/proc/self.c @@ -19,7 +19,8 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, } static const char *proc_self_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct pid_namespace *ns = inode->i_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); @@ -32,13 +33,13 @@ static const char *proc_self_get_link(struct dentry *dentry, if (unlikely(!name)) return dentry ? ERR_PTR(-ENOMEM) : ERR_PTR(-ECHILD); sprintf(name, "%d", tgid); - return *cookie = name; + set_delayed_call(done, kfree_link, name); + return name; } static const struct inode_operations proc_self_inode_operations = { .readlink = proc_self_readlink, .get_link = proc_self_get_link, - .put_link = kfree_put_link, }; static unsigned self_inum; diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c index 03eaa84604da..9eacd59e0360 100644 --- a/fs/proc/thread_self.c +++ b/fs/proc/thread_self.c @@ -20,7 +20,8 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, } static const char *proc_thread_self_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct pid_namespace *ns = inode->i_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); @@ -34,13 +35,13 @@ static const char *proc_thread_self_get_link(struct dentry *dentry, if (unlikely(!name)) return dentry ? ERR_PTR(-ENOMEM) : ERR_PTR(-ECHILD); sprintf(name, "%d/task/%d", tgid, pid); - return *cookie = name; + set_delayed_call(done, kfree_link, name); + return name; } static const struct inode_operations proc_thread_self_inode_operations = { .readlink = proc_thread_self_readlink, .get_link = proc_thread_self_get_link, - .put_link = kfree_put_link, }; static unsigned thread_self_inum; diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index ecbf11e961ab..2a12d46d7fb4 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -1666,7 +1666,6 @@ const struct inode_operations reiserfs_dir_inode_operations = { const struct inode_operations reiserfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .setattr = reiserfs_setattr, .setxattr = reiserfs_setxattr, .getxattr = reiserfs_getxattr, diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c index 7c635a5da783..dbcc2f54bad4 100644 --- a/fs/squashfs/symlink.c +++ b/fs/squashfs/symlink.c @@ -120,7 +120,6 @@ const struct address_space_operations squashfs_symlink_aops = { const struct inode_operations squashfs_symlink_inode_ops = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getxattr = generic_getxattr, .listxattr = squashfs_listxattr }; diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 80a40bcb721c..07ac18c355e7 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -147,7 +147,6 @@ static inline void write3byte(struct sysv_sb_info *sbi, static const struct inode_operations sysv_symlink_inode_operations = { .readlink = generic_readlink, .get_link = page_get_link, - .put_link = page_put_link, .getattr = sysv_getattr, }; diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index f638fd58b5b3..06eafafe636e 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -417,7 +417,7 @@ STATIC const char * xfs_vn_get_link( struct dentry *dentry, struct inode *inode, - void **cookie) + struct delayed_call *done) { char *link; int error = -ENOMEM; @@ -433,7 +433,8 @@ xfs_vn_get_link( if (unlikely(error)) goto out_kfree; - return *cookie = link; + set_delayed_call(done, kfree_link, link); + return link; out_kfree: kfree(link); @@ -1177,7 +1178,6 @@ static const struct inode_operations xfs_dir_ci_inode_operations = { static const struct inode_operations xfs_symlink_inode_operations = { .readlink = generic_readlink, .get_link = xfs_vn_get_link, - .put_link = kfree_put_link, .getattr = xfs_vn_getattr, .setattr = xfs_vn_setattr, .setxattr = generic_setxattr, diff --git a/include/linux/delayed_call.h b/include/linux/delayed_call.h new file mode 100644 index 000000000000..f7fa76ae1a9b --- /dev/null +++ b/include/linux/delayed_call.h @@ -0,0 +1,34 @@ +#ifndef _DELAYED_CALL_H +#define _DELAYED_CALL_H + +/* + * Poor man's closures; I wish we could've done them sanely polymorphic, + * but... + */ + +struct delayed_call { + void (*fn)(void *); + void *arg; +}; + +#define DEFINE_DELAYED_CALL(name) struct delayed_call name = {NULL, NULL} + +/* I really wish we had closures with sane typechecking... */ +static inline void set_delayed_call(struct delayed_call *call, + void (*fn)(void *), void *arg) +{ + call->fn = fn; + call->arg = arg; +} + +static inline void do_delayed_call(struct delayed_call *call) +{ + if (call->fn) + call->fn(call->arg); +} + +static inline void clear_delayed_call(struct delayed_call *call) +{ + call->fn = NULL; +} +#endif diff --git a/include/linux/fs.h b/include/linux/fs.h index 138e206df2fc..5de5edb01e70 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -1633,12 +1634,11 @@ struct file_operations { struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); - const char * (*get_link) (struct dentry *, struct inode *, void **); + const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *); int (*permission) (struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink) (struct dentry *, char __user *,int); - void (*put_link) (struct inode *, void *); int (*create) (struct inode *,struct dentry *, umode_t, bool); int (*link) (struct dentry *,struct inode *,struct dentry *); @@ -2736,13 +2736,14 @@ 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 const char *page_get_link(struct dentry *, struct inode *, void **); -extern void page_put_link(struct inode *, void *); +extern const char *page_get_link(struct dentry *, struct inode *, + struct delayed_call *); +extern void page_put_link(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 inode *, void *); +extern void kfree_link(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); @@ -2753,7 +2754,8 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes); void inode_sub_bytes(struct inode *inode, loff_t bytes); loff_t inode_get_bytes(struct inode *inode); void inode_set_bytes(struct inode *inode, loff_t bytes); -const char *simple_get_link(struct dentry *, struct inode *, void **); +const char *simple_get_link(struct dentry *, struct inode *, + struct delayed_call *); extern const struct inode_operations simple_symlink_inode_operations; extern int iterate_dir(struct file *, struct dir_context *); diff --git a/mm/shmem.c b/mm/shmem.c index 0605716aee06..bab9041b1967 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2496,8 +2496,15 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return 0; } +static void shmem_put_link(void *arg) +{ + mark_page_accessed(arg); + put_page(arg); +} + static const char *shmem_get_link(struct dentry *dentry, - struct inode *inode, void **cookie) + struct inode *inode, + struct delayed_call *done) { struct page *page = NULL; int error; @@ -2515,17 +2522,10 @@ static const char *shmem_get_link(struct dentry *dentry, return ERR_PTR(error); unlock_page(page); } - *cookie = page; + set_delayed_call(done, shmem_put_link, page); return page_address(page); } -static void shmem_put_link(struct inode *unused, void *cookie) -{ - struct page *page = cookie; - mark_page_accessed(page); - page_cache_release(page); -} - #ifdef CONFIG_TMPFS_XATTR /* * Superblocks without xattr inode operations may get some security.* xattr @@ -2680,7 +2680,6 @@ static const struct inode_operations shmem_short_symlink_operations = { static const struct inode_operations shmem_symlink_inode_operations = { .readlink = generic_readlink, .get_link = shmem_get_link, - .put_link = shmem_put_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, .getxattr = shmem_getxattr, -- cgit v1.2.3