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 <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2015-11-17 10:20:54 -05:00
parent 21fc61c73c
commit 6b2553918d
45 changed files with 234 additions and 132 deletions

View File

@ -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)

View File

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

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,
};

View File

@ -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
};

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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 *);

View File

@ -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);

View File

@ -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,
};

View File

@ -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,

View File

@ -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;
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,
};

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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,
};

View File

@ -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);

View File

@ -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,
};

View File

@ -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,

View File

@ -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,
};

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,

View File

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

View File

@ -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,
};

View File

@ -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,

View File

@ -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,

View File

@ -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 *);

View File

@ -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,