diff options
| author | Al Viro <viro@zeniv.linux.org.uk> | 2015-12-29 15:58:39 -0500 | 
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-12-30 13:01:03 -0500 | 
| commit | fceef393a538134f03b778c5d2519e670269342f (patch) | |
| tree | cd43c9afdc07852d286965ad4d11772f6c275d1a /fs | |
| parent | cd3417c8fc9504cc1afe944515f338aff9ec286b (diff) | |
switch ->get_link() to delayed_call, kill ->put_link()
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
36 files changed, 130 insertions, 180 deletions
| 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, | 
