diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 45 |
1 files changed, 28 insertions, 17 deletions
@@ -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); |