|
@@ -501,6 +501,7 @@ struct path {
|
|
static inline int __do_follow_link(struct path *path, struct nameidata *nd)
|
|
static inline int __do_follow_link(struct path *path, struct nameidata *nd)
|
|
{
|
|
{
|
|
int error;
|
|
int error;
|
|
|
|
+ void *cookie;
|
|
struct dentry *dentry = path->dentry;
|
|
struct dentry *dentry = path->dentry;
|
|
|
|
|
|
touch_atime(path->mnt, dentry);
|
|
touch_atime(path->mnt, dentry);
|
|
@@ -508,13 +509,15 @@ static inline int __do_follow_link(struct path *path, struct nameidata *nd)
|
|
|
|
|
|
if (path->mnt == nd->mnt)
|
|
if (path->mnt == nd->mnt)
|
|
mntget(path->mnt);
|
|
mntget(path->mnt);
|
|
- error = dentry->d_inode->i_op->follow_link(dentry, nd);
|
|
|
|
- if (!error) {
|
|
|
|
|
|
+ cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
|
|
|
|
+ error = PTR_ERR(cookie);
|
|
|
|
+ if (!IS_ERR(cookie)) {
|
|
char *s = nd_get_link(nd);
|
|
char *s = nd_get_link(nd);
|
|
|
|
+ error = 0;
|
|
if (s)
|
|
if (s)
|
|
error = __vfs_follow_link(nd, s);
|
|
error = __vfs_follow_link(nd, s);
|
|
if (dentry->d_inode->i_op->put_link)
|
|
if (dentry->d_inode->i_op->put_link)
|
|
- dentry->d_inode->i_op->put_link(dentry, nd);
|
|
|
|
|
|
+ dentry->d_inode->i_op->put_link(dentry, nd, cookie);
|
|
}
|
|
}
|
|
dput(dentry);
|
|
dput(dentry);
|
|
mntput(path->mnt);
|
|
mntput(path->mnt);
|
|
@@ -2344,15 +2347,17 @@ out:
|
|
int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
|
int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
|
{
|
|
{
|
|
struct nameidata nd;
|
|
struct nameidata nd;
|
|
- int res;
|
|
|
|
|
|
+ void *cookie;
|
|
|
|
+
|
|
nd.depth = 0;
|
|
nd.depth = 0;
|
|
- res = dentry->d_inode->i_op->follow_link(dentry, &nd);
|
|
|
|
- if (!res) {
|
|
|
|
- res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
|
|
|
|
|
|
+ cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
|
|
|
|
+ if (!IS_ERR(cookie)) {
|
|
|
|
+ int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
|
|
if (dentry->d_inode->i_op->put_link)
|
|
if (dentry->d_inode->i_op->put_link)
|
|
- dentry->d_inode->i_op->put_link(dentry, &nd);
|
|
|
|
|
|
+ dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
|
|
|
|
+ cookie = ERR_PTR(res);
|
|
}
|
|
}
|
|
- return res;
|
|
|
|
|
|
+ return PTR_ERR(cookie);
|
|
}
|
|
}
|
|
|
|
|
|
int vfs_follow_link(struct nameidata *nd, const char *link)
|
|
int vfs_follow_link(struct nameidata *nd, const char *link)
|
|
@@ -2395,23 +2400,20 @@ int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
|
return res;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
|
|
-int page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
|
|
|
|
|
|
+void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
|
|
{
|
|
{
|
|
- struct page *page;
|
|
|
|
|
|
+ struct page *page = NULL;
|
|
nd_set_link(nd, page_getlink(dentry, &page));
|
|
nd_set_link(nd, page_getlink(dentry, &page));
|
|
- return 0;
|
|
|
|
|
|
+ return page;
|
|
}
|
|
}
|
|
|
|
|
|
-void page_put_link(struct dentry *dentry, struct nameidata *nd)
|
|
|
|
|
|
+void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
|
|
{
|
|
{
|
|
- if (!IS_ERR(nd_get_link(nd))) {
|
|
|
|
- struct page *page;
|
|
|
|
- page = find_get_page(dentry->d_inode->i_mapping, 0);
|
|
|
|
- if (!page)
|
|
|
|
- BUG();
|
|
|
|
|
|
+ struct page *page = cookie;
|
|
|
|
+
|
|
|
|
+ if (page) {
|
|
kunmap(page);
|
|
kunmap(page);
|
|
page_cache_release(page);
|
|
page_cache_release(page);
|
|
- page_cache_release(page);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|