diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 92 |
1 files changed, 91 insertions, 1 deletions
diff --git a/fs/namei.c b/fs/namei.c index eba55a751..95ee1c127 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -16,6 +16,7 @@ #include <linux/proc_fs.h> #include <linux/smp_lock.h> #include <linux/quotaops.h> +#include <linux/pagemap.h> #include <asm/uaccess.h> #include <asm/unaligned.h> @@ -283,7 +284,7 @@ static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry if ((follow & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { - if (current->link_count < 5) { + if (current->link_count < 32) { struct dentry * result; current->link_count++; @@ -1419,3 +1420,92 @@ asmlinkage long sys_rename(const char * oldname, const char * newname) unlock_kernel(); return error; } + +int vfs_readlink(struct dentry *dentry, char *buffer, int buflen, char *link) +{ + u32 len; + + len = PTR_ERR(link); + if (IS_ERR(link)) + goto out; + + len = strlen(link); + if (len > buflen) + len = buflen; + copy_to_user(buffer, link, len); +out: + return len; +} + +static inline struct dentry * +__vfs_follow_link(struct dentry *dentry, struct dentry *base, + unsigned follow, char *link) +{ + struct dentry *result; + UPDATE_ATIME(dentry->d_inode); + + if (IS_ERR(link)) + goto fail; + + result = lookup_dentry(link, base, follow); + return result; + +fail: + dput(base); + return (struct dentry *)link; +} + +struct dentry * +vfs_follow_link(struct dentry *dentry, struct dentry *base, +unsigned int follow, char *link) +{ + return __vfs_follow_link(dentry,base,follow,link); +} + +/* get the link contents into pagecache */ +static char *page_getlink(struct dentry * dentry, struct page **ppage) +{ + struct page * page; + page = read_cache_page(&dentry->d_inode->i_data, 0, + (filler_t *)dentry->d_inode->i_op->readpage, + dentry); + if (IS_ERR(page)) + goto sync_fail; + wait_on_page(page); + if (!Page_Uptodate(page)) + goto async_fail; + *ppage = page; + return (char*) kmap(page); + +async_fail: + page_cache_release(page); + return ERR_PTR(-EIO); + +sync_fail: + return (char*)page; +} + +int page_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + struct page *page = NULL; + char *s = page_getlink(dentry, &page); + int res = vfs_readlink(dentry,buffer,buflen,s); + if (page) { + kunmap(page); + page_cache_release(page); + } + return res; +} + +struct dentry * +page_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow) +{ + struct page *page = NULL; + char *s = page_getlink(dentry, &page); + struct dentry *res = __vfs_follow_link(dentry,base,follow,s); + if (page) { + kunmap(page); + page_cache_release(page); + } + return res; +} |