summaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c92
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;
+}