summaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2015-11-17 16:20:54 +0100
committerAl Viro <viro@zeniv.linux.org.uk>2015-12-09 04:41:54 +0100
commit6b2553918d8b4e6de9853fd6315bec7271a2e592 (patch)
tree85540dcb0dc0de3d67c68d0aa7b17058f4e96539 /fs/namei.c
parentdon't put symlink bodies in pagecache into highmem (diff)
downloadlinux-6b2553918d8b4e6de9853fd6315bec7271a2e592.tar.xz
linux-6b2553918d8b4e6de9853fd6315bec7271a2e592.zip
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>
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c45
1 files changed, 28 insertions, 17 deletions
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);