diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2010-06-06 21:51:16 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-08-09 16:48:12 -0400 |
commit | d0352d3ed722b134dacc21836c1763e7e3523662 (patch) | |
tree | 030c031e5defb8c496f1b4766056c26e5460558a | |
parent | c5322220eb91b9e56ac7b69eb690d9d20fac5725 (diff) |
hostfs: sanitize symlinks
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/hostfs/hostfs_kern.c | 61 |
1 files changed, 35 insertions, 26 deletions
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 3841fb1ca5a..10bb71b1548 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/seq_file.h> #include <linux/mount.h> +#include <linux/namei.h> #include "hostfs.h" #include "init.h" #include "kern.h" @@ -48,7 +49,7 @@ static int append = 0; static const struct inode_operations hostfs_iops; static const struct inode_operations hostfs_dir_iops; -static const struct address_space_operations hostfs_link_aops; +static const struct inode_operations hostfs_link_iops; #ifndef MODULE static int __init hostfs_args(char *options, int *add) @@ -471,8 +472,7 @@ static int read_name(struct inode *ino, char *name) switch (st.mode & S_IFMT) { case S_IFLNK: - ino->i_op = &page_symlink_inode_operations; - ino->i_mapping->a_ops = &hostfs_link_aops; + ino->i_op = &hostfs_link_iops; break; case S_IFDIR: ino->i_op = &hostfs_dir_iops; @@ -835,32 +835,41 @@ static const struct inode_operations hostfs_dir_iops = { .setattr = hostfs_setattr, }; -int hostfs_link_readpage(struct file *file, struct page *page) -{ - char *buffer, *name; - int err; - - buffer = kmap(page); - name = inode_name(page->mapping->host); - if (name == NULL) - return -ENOMEM; - err = hostfs_do_readlink(name, buffer, PAGE_CACHE_SIZE); - kfree(name); - if (err == PAGE_CACHE_SIZE) - err = -E2BIG; - else if (err > 0) { - flush_dcache_page(page); - SetPageUptodate(page); - if (PageError(page)) ClearPageError(page); - err = 0; +static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *link = __getname(); + if (link) { + char *path = dentry_name(dentry); + int err = -ENOMEM; + if (path) { + int err = hostfs_do_readlink(path, link, PATH_MAX); + if (err == PATH_MAX) + err = -E2BIG; + kfree(path); + } + if (err < 0) { + __putname(link); + link = ERR_PTR(err); + } + } else { + link = ERR_PTR(-ENOMEM); } - kunmap(page); - unlock_page(page); - return err; + + nd_set_link(nd, link); + return NULL; +} + +static void hostfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +{ + char *s = nd_get_link(nd); + if (!IS_ERR(s)) + __putname(s); } -static const struct address_space_operations hostfs_link_aops = { - .readpage = hostfs_link_readpage, +static const struct inode_operations hostfs_link_iops = { + .readlink = generic_readlink, + .follow_link = hostfs_follow_link, + .put_link = hostfs_put_link, }; static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) |