lkml.org 
[lkml]   [1999]   [Dec]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: mmap on a device returns ENODEV


    On Wed, 8 Dec 1999, Alexander Viro wrote:

    [snip]

    > Geez... Thanks. Actually I'ld rather finish this story with the
    > symlinks-in-pagecache patch first. Could you comment on the variant I've
    > sent to you yesterday? It applies to 2.3.31 and seems to work here. I can
    > rediff it if you want but that would differ only in one-line offset at
    > fs/ext2/inode.c

    Argh. OK, version of the patch against -pre2 attached.
    Al
    diff -urN linux-2.3.32-pre2/fs/autofs/symlink.c linux-bird.symlink/fs/autofs/symlink.c
    --- linux-2.3.32-pre2/fs/autofs/symlink.c Mon Dec 6 22:06:00 1999
    +++ linux-bird.symlink/fs/autofs/symlink.c Wed Dec 8 20:19:26 1999
    @@ -10,49 +10,21 @@
    *
    * ------------------------------------------------------------------------- */

    -#include <linux/string.h>
    -#include <linux/sched.h>
    #include "autofs_i.h"

    static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen)
    {
    - struct autofs_symlink *sl;
    - int len;
    -
    - sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip;
    - len = sl->len;
    - if (len > buflen) len = buflen;
    - copy_to_user(buffer, sl->data, len);
    - return len;
    + char *s=((struct autofs_symlink *)dentry->d_inode->u.generic_ip)->data;
    + return vfs_readlink(dentry, buffer, buflen, s);
    }

    -static struct dentry * autofs_follow_link(struct dentry *dentry,
    - struct dentry *base,
    - unsigned int follow)
    +static struct dentry *autofs_follow_link(struct dentry *dentry, struct dentry *base, unsigned flags)
    {
    - struct autofs_symlink *sl;
    -
    - sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip;
    - return lookup_dentry(sl->data, base, follow);
    + char *s=((struct autofs_symlink *)dentry->d_inode->u.generic_ip)->data;
    + return vfs_follow_link(dentry, base, flags, s);
    }

    struct inode_operations autofs_symlink_inode_operations = {
    - NULL, /* file operations */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - autofs_readlink, /* readlink */
    - autofs_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    + readlink: autofs_readlink,
    + follow_link: autofs_follow_link
    };
    diff -urN linux-2.3.32-pre2/fs/buffer.c linux-bird.symlink/fs/buffer.c
    --- linux-2.3.32-pre2/fs/buffer.c Mon Dec 6 22:06:01 1999
    +++ linux-bird.symlink/fs/buffer.c Wed Dec 8 20:19:26 1999
    @@ -1378,11 +1378,10 @@
    return err;
    }

    -int block_write_range(struct dentry *dentry, struct page *page,
    +int block_write_zero_range(struct inode *inode, struct page *page,
    unsigned zerofrom, unsigned from, unsigned to,
    const char * buf)
    {
    - struct inode *inode = dentry->d_inode;
    unsigned zeroto = 0, block_start, block_end;
    unsigned long block;
    int err = 0, partial = 0, need_balance_dirty = 0;
    @@ -1504,7 +1503,7 @@

    int block_write_partial_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
    {
    - struct dentry *dentry = file->f_dentry;
    + struct inode *inode = file->f_dentry->d_inode;
    int err;

    if (!PageLocked(page))
    @@ -1514,7 +1513,7 @@
    if (bytes+offset < 0 || bytes+offset > PAGE_SIZE)
    BUG();

    - err = block_write_range(dentry, page, offset,offset,offset+bytes, buf);
    + err = block_write_range(inode, page, offset, bytes, buf);
    return err ? err : bytes;
    }

    @@ -1525,8 +1524,7 @@

    int block_write_cont_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
    {
    - struct dentry *dentry = file->f_dentry;
    - struct inode *inode = dentry->d_inode;
    + struct inode *inode = file->f_dentry->d_inode;
    int err;
    unsigned zerofrom = offset;

    @@ -1535,7 +1533,8 @@
    else if (page->index == (inode->i_size >> PAGE_CACHE_SHIFT) &&
    offset > (inode->i_size & ~PAGE_CACHE_MASK))
    zerofrom = inode->i_size & ~PAGE_CACHE_MASK;
    - err = block_write_range(dentry, page, zerofrom,offset,offset+bytes,buf);
    + err = block_write_zero_range(inode, page, zerofrom,offset,offset+bytes,
    + buf);
    return err ? err : bytes;
    }

    @@ -1829,9 +1828,8 @@
    * mark_buffer_uptodate() functions propagate buffer state into the
    * page struct once IO has completed.
    */
    -int block_read_full_page(struct dentry * dentry, struct page * page)
    +static inline int __block_read_full_page(struct inode *inode, struct page *page)
    {
    - struct inode *inode = dentry->d_inode;
    unsigned long iblock;
    struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
    unsigned int blocksize, blocks;
    @@ -1888,6 +1886,47 @@
    if (kaddr)
    kunmap(page);
    return 0;
    +}
    +
    +int block_read_full_page(struct dentry *dentry, struct page *page)
    +{
    + return __block_read_full_page(dentry->d_inode, page);
    +}
    +
    +int block_symlink(struct inode *inode, const char *symname, int len)
    +{
    + struct page *page = grab_cache_page(&inode->i_data, 0);
    + mm_segment_t fs;
    + int err = -ENOMEM;
    +
    + if (!page)
    + goto fail;
    + fs = get_fs();
    + set_fs(KERNEL_DS);
    + err = block_write_range(inode, page, 0, len-1, symname);
    + set_fs(fs);
    + inode->i_size = len-1;
    + if (err)
    + goto fail_write;
    + /*
    + * Notice that we are _not_ going to block here - end of page is
    + * unmapped, so this will only try to map the rest of page, see
    + * that it is unmapped (typically even will not look into inode -
    + * ->i_size will be enough for everything) and zero it out.
    + * OTOH it's obviously correct and should make the page up-to-date.
    + */
    + err = __block_read_full_page(inode, page);
    + wait_on_page(page);
    + page_cache_release(page);
    + if (err < 0)
    + goto fail;
    + mark_inode_dirty(inode);
    + return 0;
    +fail_write:
    + UnlockPage(page);
    + page_cache_release(page);
    +fail:
    + return err;
    }

    /*
    diff -urN linux-2.3.32-pre2/fs/coda/symlink.c linux-bird.symlink/fs/coda/symlink.c
    --- linux-2.3.32-pre2/fs/coda/symlink.c Mon Dec 6 22:06:01 1999
    +++ linux-bird.symlink/fs/coda/symlink.c Wed Dec 8 20:19:26 1999
    @@ -14,9 +14,6 @@
    #include <linux/stat.h>
    #include <linux/errno.h>
    #include <linux/locks.h>
    -#include <asm/segment.h>
    -#include <asm/uaccess.h>
    -#include <linux/string.h>

    #include <linux/coda.h>
    #include <linux/coda_linux.h>
    @@ -25,97 +22,34 @@
    #include <linux/coda_cache.h>
    #include <linux/coda_proc.h>

    -static int coda_readlink(struct dentry *de, char *buffer, int length);
    -static struct dentry *coda_follow_link(struct dentry *, struct dentry *,
    - unsigned int);
    -
    -struct inode_operations coda_symlink_inode_operations = {
    - NULL, /* no file-operations */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - coda_readlink, /* readlink */
    - coda_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    -};
    -
    -static int coda_readlink(struct dentry *de, char *buffer, int length)
    +static int coda_symlink_filler(struct dentry *dentry, struct page *page)
    {
    - struct inode *inode = de->d_inode;
    - int len;
    - int error;
    - char *buf;
    - struct coda_inode_info *cp;
    - ENTRY;
    -
    - cp = ITOC(inode);
    - coda_vfs_stat.readlink++;
    -
    - /* the maximum length we receive is len */
    - if ( length > CODA_MAXPATHLEN )
    - len = CODA_MAXPATHLEN;
    - else
    - len = length;
    - CODA_ALLOC(buf, char *, len);
    - if ( !buf )
    - return -ENOMEM;
    -
    - error = venus_readlink(inode->i_sb, &(cp->c_fid), buf, &len);
    -
    - CDEBUG(D_INODE, "result %s\n", buf);
    - if (! error) {
    - copy_to_user(buffer, buf, len);
    - put_user('\0', buffer + len);
    - error = len;
    - }
    - if ( buf )
    - CODA_FREE(buf, len);
    - return error;
    -}
    -
    -static struct dentry *coda_follow_link(struct dentry *de, struct dentry *base,
    - unsigned int follow)
    -{
    - struct inode *inode = de->d_inode;
    + struct inode *inode = dentry->d_inode;
    int error;
    struct coda_inode_info *cnp;
    - unsigned int len;
    - char mem[CODA_MAXPATHLEN];
    - char *path;
    - ENTRY;
    - CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino);
    -
    + unsigned int len = PAGE_SIZE;
    + char *p = (char*)kmap(page);
    +
    cnp = ITOC(inode);
    coda_vfs_stat.follow_link++;

    - len = CODA_MAXPATHLEN;
    - error = venus_readlink(inode->i_sb, &(cnp->c_fid), mem, &len);
    -
    - if (error) {
    - dput(base);
    - return ERR_PTR(error);
    - }
    - len = strlen(mem);
    - path = kmalloc(len + 1, GFP_KERNEL);
    - if (!path) {
    - dput(base);
    - return ERR_PTR(-ENOMEM);
    - }
    - memcpy(path, mem, len);
    - path[len] = 0;
    -
    - base = lookup_dentry(path, base, follow);
    - kfree(path);
    - return base;
    + error = venus_readlink(inode->i_sb, &(cnp->c_fid), p, &len);
    + if (error)
    + goto fail;
    + SetPageUptodate(page);
    + kunmap(page);
    + UnlockPage(page);
    + return 0;
    +
    +fail:
    + SetPageError(page);
    + kunmap(page);
    + UnlockPage(page);
    + return error;
    }
    +
    +struct inode_operations coda_symlink_inode_operations = {
    + readlink: page_readlink,
    + follow_link: page_follow_link,
    + readpage: coda_symlink_filler
    +};
    diff -urN linux-2.3.32-pre2/fs/ext2/inode.c linux-bird.symlink/fs/ext2/inode.c
    --- linux-2.3.32-pre2/fs/ext2/inode.c Wed Dec 8 16:59:02 1999
    +++ linux-bird.symlink/fs/ext2/inode.c Wed Dec 8 20:19:26 1999
    @@ -713,7 +713,9 @@
    else if (S_ISDIR(inode->i_mode))
    inode->i_op = &ext2_dir_inode_operations;
    else if (S_ISLNK(inode->i_mode))
    - inode->i_op = &ext2_symlink_inode_operations;
    + inode->i_op = inode->i_blocks
    + ?&ext2_symlink_inode_operations
    + :&ext2_fast_symlink_inode_operations;
    else
    init_special_inode(inode, inode->i_mode,
    le32_to_cpu(raw_inode->i_block[0]));
    diff -urN linux-2.3.32-pre2/fs/ext2/namei.c linux-bird.symlink/fs/ext2/namei.c
    --- linux-2.3.32-pre2/fs/ext2/namei.c Mon Dec 6 22:06:02 1999
    +++ linux-bird.symlink/fs/ext2/namei.c Wed Dec 8 20:19:26 1999
    @@ -677,48 +677,32 @@

    int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname)
    {
    - struct ext2_dir_entry_2 * de;
    struct inode * inode;
    - struct buffer_head * bh = NULL, * name_block = NULL;
    - char * link;
    - int i, l, err = -EIO;
    - char c;
    + struct ext2_dir_entry_2 * de;
    + struct buffer_head * bh = NULL;
    + int l, err;

    - if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
    - return err;
    - }
    - inode->i_mode = S_IFLNK | S_IRWXUGO;
    - inode->i_op = &ext2_symlink_inode_operations;
    - for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
    - symname [l]; l++)
    - ;
    - if (l >= sizeof (inode->u.ext2_i.i_data)) {
    + err = -ENAMETOOLONG;
    + l = strlen(symname)+1;
    + if (l > dir->i_sb->s_blocksize)
    + goto out;

    - ext2_debug ("l=%d, normal symlink\n", l);
    + err = -EIO;
    + if (!(inode = ext2_new_inode (dir, S_IFLNK, &err)))
    + goto out;

    - name_block = ext2_bread (inode, 0, 1, &err);
    - if (!name_block) {
    - inode->i_nlink--;
    - mark_inode_dirty(inode);
    - iput (inode);
    - return err;
    - }
    - link = name_block->b_data;
    - } else {
    - link = (char *) inode->u.ext2_i.i_data;
    -
    - ext2_debug ("l=%d, fast symlink\n", l);
    + inode->i_mode = S_IFLNK | S_IRWXUGO;

    + if (l > sizeof (inode->u.ext2_i.i_data)) {
    + inode->i_op = &ext2_symlink_inode_operations;
    + err = block_symlink(inode, symname, l);
    + if (err)
    + goto out_no_entry;
    + } else {
    + inode->i_op = &ext2_fast_symlink_inode_operations;
    + memcpy((char*)&inode->u.ext2_i.i_data,symname,l);
    + inode->i_size = l-1;
    }
    - i = 0;
    - while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
    - link[i++] = c;
    - link[i] = 0;
    - if (name_block) {
    - mark_buffer_dirty(name_block, 1);
    - brelse (name_block);
    - }
    - inode->i_size = i;
    mark_inode_dirty(inode);

    bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
    diff -urN linux-2.3.32-pre2/fs/ext2/symlink.c linux-bird.symlink/fs/ext2/symlink.c
    --- linux-2.3.32-pre2/fs/ext2/symlink.c Mon Dec 6 22:06:02 1999
    +++ linux-bird.symlink/fs/ext2/symlink.c Wed Dec 8 20:19:26 1999
    @@ -16,89 +16,28 @@
    */

    #include <linux/fs.h>
    -#include <asm/uaccess.h>
    +#include <linux/ext2_fs.h>

    -
    -
    -static int ext2_readlink (struct dentry *, char *, int);
    -static struct dentry *ext2_follow_link(struct dentry *, struct dentry *, unsigned int);
    -
    -/*
    - * symlinks can't do much...
    - */
    -struct inode_operations ext2_symlink_inode_operations = {
    - NULL, /* no file-operations */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - ext2_readlink, /* readlink */
    - ext2_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    -};
    -
    -static struct dentry * ext2_follow_link(struct dentry * dentry,
    - struct dentry *base,
    - unsigned int follow)
    +static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen)
    {
    - struct inode *inode = dentry->d_inode;
    - struct buffer_head * bh = NULL;
    - int error;
    - char * link;
    -
    - link = (char *) inode->u.ext2_i.i_data;
    - if (inode->i_blocks) {
    - if (!(bh = ext2_bread (inode, 0, 0, &error))) {
    - dput(base);
    - return ERR_PTR(-EIO);
    - }
    - link = bh->b_data;
    - }
    - UPDATE_ATIME(inode);
    - base = lookup_dentry(link, base, follow);
    - if (bh)
    - brelse(bh);
    - return base;
    + char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
    + return vfs_readlink(dentry, buffer, buflen, s);
    }

    -static int ext2_readlink (struct dentry * dentry, char * buffer, int buflen)
    +static struct dentry *ext2_follow_link(struct dentry *dentry, struct dentry *base, unsigned flags)
    {
    - struct inode *inode = dentry->d_inode;
    - struct buffer_head * bh = NULL;
    - char * link;
    - int i;
    -
    - if (buflen > inode->i_sb->s_blocksize - 1)
    - buflen = inode->i_sb->s_blocksize - 1;
    + char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
    + return vfs_follow_link(dentry, base, flags, s);
    +}

    - link = (char *) inode->u.ext2_i.i_data;
    - if (inode->i_blocks) {
    - int err;
    - bh = ext2_bread (inode, 0, 0, &err);
    - if (!bh) {
    - if(err < 0) /* indicate type of error */
    - return err;
    - return 0;
    - }
    - link = bh->b_data;
    - }
    +struct inode_operations ext2_fast_symlink_inode_operations = {
    + readlink: ext2_readlink,
    + follow_link: ext2_follow_link,
    +};

    - i = 0;
    - while (i < buflen && link[i])
    - i++;
    - if (copy_to_user(buffer, link, i))
    - i = -EFAULT;
    - if (bh)
    - brelse (bh);
    - return i;
    -}
    +struct inode_operations ext2_symlink_inode_operations = {
    + readlink: page_readlink,
    + follow_link: page_follow_link,
    + get_block: ext2_get_block,
    + readpage: block_read_full_page,
    +};
    diff -urN linux-2.3.32-pre2/fs/minix/namei.c linux-bird.symlink/fs/minix/namei.c
    --- linux-2.3.32-pre2/fs/minix/namei.c Sun Sep 12 20:25:20 1999
    +++ linux-bird.symlink/fs/minix/namei.c Wed Dec 8 20:19:26 1999
    @@ -461,46 +461,43 @@
    {
    struct minix_dir_entry * de;
    struct inode * inode = NULL;
    - struct buffer_head * bh = NULL, * name_block = NULL;
    + struct buffer_head * bh = NULL;
    int i;
    - char c;
    + int err;

    - inode = minix_new_inode(dir, &i);
    - if (i)
    - return i;
    + err = -ENAMETOOLONG;
    + i = strlen(symname)+1;
    + if (i>1024)
    + goto out;
    + inode = minix_new_inode(dir, &err);
    + if (err)
    + goto out;
    + err = -ENOSPC;
    if (!inode)
    - return -ENOSPC;
    + goto out;

    inode->i_mode = S_IFLNK | 0777;
    inode->i_op = &minix_symlink_inode_operations;
    - name_block = minix_bread(inode,0,1);
    - if (!name_block) {
    - inode->i_nlink--;
    - mark_inode_dirty(inode);
    - iput(inode);
    - return -ENOSPC;
    - }
    - i = 0;
    - while (i < 1023 && (c=*(symname++)))
    - name_block->b_data[i++] = c;
    - name_block->b_data[i] = 0;
    - mark_buffer_dirty(name_block, 1);
    - brelse(name_block);
    - inode->i_size = i;
    - mark_inode_dirty(inode);
    - i = minix_add_entry(dir, dentry->d_name.name,
    + err = block_symlink(inode, symname, i);
    + if (err)
    + goto fail;
    +
    + err = minix_add_entry(dir, dentry->d_name.name,
    dentry->d_name.len, &bh, &de);
    - if (i) {
    - inode->i_nlink--;
    - mark_inode_dirty(inode);
    - iput(inode);
    - return i;
    - }
    + if (err)
    + goto fail;
    +
    de->inode = inode->i_ino;
    mark_buffer_dirty(bh, 1);
    brelse(bh);
    d_instantiate(dentry, inode);
    - return 0;
    +out:
    + return err;
    +fail:
    + inode->i_nlink--;
    + mark_inode_dirty(inode);
    + iput(inode);
    + goto out;
    }

    int minix_link(struct dentry * old_dentry, struct inode * dir,
    diff -urN linux-2.3.32-pre2/fs/minix/symlink.c linux-bird.symlink/fs/minix/symlink.c
    --- linux-2.3.32-pre2/fs/minix/symlink.c Mon Dec 6 22:06:03 1999
    +++ linux-bird.symlink/fs/minix/symlink.c Wed Dec 8 20:19:26 1999
    @@ -4,77 +4,19 @@
    * Copyright (C) 1991, 1992 Linus Torvalds
    *
    * minix symlink handling code
    + *
    + * Code removed. 1999, AV ;-)
    */

    -#include <linux/errno.h>
    -#include <linux/sched.h>
    #include <linux/fs.h>
    #include <linux/minix_fs.h>
    -#include <linux/stat.h>
    -
    -#include <asm/uaccess.h>
    -
    -static int minix_readlink(struct dentry *, char *, int);
    -static struct dentry *minix_follow_link(struct dentry *, struct dentry *, unsigned int);

    /*
    * symlinks can't do much...
    */
    struct inode_operations minix_symlink_inode_operations = {
    - NULL, /* no file-operations */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - minix_readlink, /* readlink */
    - minix_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    + readlink: page_readlink,
    + follow_link: page_follow_link,
    + get_block: minix_get_block,
    + readpage: block_read_full_page
    };
    -
    -static struct dentry * minix_follow_link(struct dentry * dentry,
    - struct dentry * base,
    - unsigned int follow)
    -{
    - struct inode *inode = dentry->d_inode;
    - struct buffer_head * bh;
    -
    - bh = minix_bread(inode, 0, 0);
    - if (!bh) {
    - dput(base);
    - return ERR_PTR(-EIO);
    - }
    - UPDATE_ATIME(inode);
    - base = lookup_dentry(bh->b_data, base, follow);
    - brelse(bh);
    - return base;
    -}
    -
    -static int minix_readlink(struct dentry * dentry, char * buffer, int buflen)
    -{
    - struct buffer_head * bh;
    - int i;
    - char c;
    -
    - if (buflen > 1023)
    - buflen = 1023;
    - bh = minix_bread(dentry->d_inode, 0, 0);
    - if (!bh)
    - return 0;
    - i = 0;
    - while (i<buflen && (c = bh->b_data[i])) {
    - i++;
    - put_user(c,buffer++);
    - }
    - brelse(bh);
    - return i;
    -}
    diff -urN linux-2.3.32-pre2/fs/namei.c linux-bird.symlink/fs/namei.c
    --- linux-2.3.32-pre2/fs/namei.c Thu Oct 14 20:09:31 1999
    +++ linux-bird.symlink/fs/namei.c Wed Dec 8 20:19:26 1999
    @@ -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>
    @@ -1418,4 +1419,86 @@
    }
    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;
    +}
    +
    +struct dentry *
    +vfs_follow_link(struct dentry *dentry, struct dentry *base,
    +unsigned int 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;
    +}
    +
    +/* 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;
    }
    diff -urN linux-2.3.32-pre2/fs/nfs/dir.c linux-bird.symlink/fs/nfs/dir.c
    --- linux-2.3.32-pre2/fs/nfs/dir.c Mon Dec 6 22:06:03 1999
    +++ linux-bird.symlink/fs/nfs/dir.c Wed Dec 8 20:19:26 1999
    @@ -418,7 +418,7 @@
    {
    struct dentry *dentry = filp->f_dentry;
    struct inode *inode = dentry->d_inode;
    - struct page *page, **hash;
    + struct page *page;
    long offset;
    int res;

    @@ -432,8 +432,7 @@
    if ((offset = nfs_readdir_offset(inode, filp->f_pos)) < 0)
    goto no_dirent_page;

    - hash = page_hash(&inode->i_data, offset);
    - page = __find_get_page(&inode->i_data, offset, hash);
    + page = find_get_page(&inode->i_data, offset);
    if (!page)
    goto no_dirent_page;
    if (!Page_Uptodate(page))
    diff -urN linux-2.3.32-pre2/fs/nfs/symlink.c linux-bird.symlink/fs/nfs/symlink.c
    --- linux-2.3.32-pre2/fs/nfs/symlink.c Mon Dec 6 22:06:03 1999
    +++ linux-bird.symlink/fs/nfs/symlink.c Wed Dec 8 20:19:27 1999
    @@ -22,35 +22,6 @@
    #include <linux/malloc.h>
    #include <linux/string.h>

    -#include <asm/uaccess.h>
    -
    -static int nfs_readlink(struct dentry *, char *, int);
    -static struct dentry *nfs_follow_link(struct dentry *, struct dentry *, unsigned int);
    -
    -/*
    - * symlinks can't do much...
    - */
    -struct inode_operations nfs_symlink_inode_operations = {
    - NULL, /* no file-operations */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - nfs_readlink, /* readlink */
    - nfs_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    -};
    -
    /* Symlink caching in the page cache is even more simplistic
    * and straight-forward than readdir caching.
    */
    @@ -91,12 +62,12 @@
    if (IS_ERR(page))
    goto read_failed;
    if (!Page_Uptodate(page))
    - goto followlink_read_error;
    + goto getlink_read_error;
    *ppage = page;
    p = (u32 *) kmap(page);
    return (char*)(p+1);

    -followlink_read_error:
    +getlink_read_error:
    page_cache_release(page);
    return ERR_PTR(-EIO);
    read_failed:
    @@ -106,41 +77,31 @@
    static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
    {
    struct page *page = NULL;
    - u32 len;
    - char *s = nfs_getlink(dentry, &page);
    - UPDATE_ATIME(dentry->d_inode);
    -
    - len = PTR_ERR(s);
    - if (IS_ERR(s))
    - goto out;
    -
    - len = strlen(s);
    - if (len > buflen)
    - len = buflen;
    - copy_to_user(buffer, s, len);
    - kunmap(page);
    - page_cache_release(page);
    -out:
    - return len;
    + int res = vfs_readlink(dentry,buffer,buflen,nfs_getlink(dentry,&page));
    + if (page) {
    + kunmap(page);
    + page_cache_release(page);
    + }
    + return res;
    }

    static struct dentry *
    nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
    {
    - struct dentry *result;
    struct page *page = NULL;
    - char *s = nfs_getlink(dentry, &page);
    - UPDATE_ATIME(dentry->d_inode);
    -
    - if (IS_ERR(s))
    - goto fail;
    -
    - result = lookup_dentry(s, base, follow);
    -
    - kunmap(page);
    - page_cache_release(page);
    - return result;
    -
    -fail:
    - return (struct dentry *)s;
    + struct dentry *res = vfs_follow_link(dentry, base, follow,
    + nfs_getlink(dentry, &page));
    + if (page) {
    + kunmap(page);
    + page_cache_release(page);
    + }
    + return res;
    }
    +
    +/*
    + * symlinks can't do much...
    + */
    +struct inode_operations nfs_symlink_inode_operations = {
    + readlink: nfs_readlink,
    + follow_link: nfs_follow_link,
    +};
    diff -urN linux-2.3.32-pre2/fs/proc/generic.c linux-bird.symlink/fs/proc/generic.c
    --- linux-2.3.32-pre2/fs/proc/generic.c Wed Nov 24 05:56:19 1999
    +++ linux-bird.symlink/fs/proc/generic.c Wed Dec 8 20:19:27 1999
    @@ -199,40 +199,21 @@
    return PROC_DYNAMIC_FIRST + i;
    }

    -static int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
    +static int proc_readlink(struct dentry *dentry, char *buffer, int buflen)
    {
    - struct inode *inode = dentry->d_inode;
    - struct proc_dir_entry * de;
    - int len;
    - de = (struct proc_dir_entry *) inode->u.generic_ip;
    - len = de->size+1;
    - if (len > buflen)
    - len = buflen;
    - copy_to_user(buffer, de->data, len);
    - return len;
    + char *s=((struct proc_dir_entry *)dentry->d_inode->u.generic_ip)->data;
    + return vfs_readlink(dentry, buffer, buflen, s);
    }

    -struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
    +static struct dentry *proc_follow_link(struct dentry *dentry, struct dentry *base, unsigned flags)
    {
    - struct inode *inode = dentry->d_inode;
    - struct proc_dir_entry * de;
    - de = (struct proc_dir_entry *) inode->u.generic_ip;
    - return lookup_dentry(de->data, base, follow);
    + char *s=((struct proc_dir_entry *)dentry->d_inode->u.generic_ip)->data;
    + return vfs_follow_link(dentry, base, flags, s);
    }

    static struct inode_operations proc_link_inode_operations = {
    - NULL, /* no file-ops */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - proc_readlink, /* readlink */
    - proc_follow_link, /* follow_link */
    + readlink: proc_readlink,
    + follow_link: proc_follow_link
    };

    /*
    diff -urN linux-2.3.32-pre2/fs/proc/root.c linux-bird.symlink/fs/proc/root.c
    --- linux-2.3.32-pre2/fs/proc/root.c Wed Nov 24 05:56:19 1999
    +++ linux-bird.symlink/fs/proc/root.c Wed Dec 8 20:19:27 1999
    @@ -27,14 +27,9 @@
    */
    static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen)
    {
    - int len;
    char tmp[30];
    -
    - len = sprintf(tmp, "%d", current->pid);
    - if (buflen < len)
    - len = buflen;
    - copy_to_user(buffer, tmp, len);
    - return len;
    + sprintf(tmp, "%d", current->pid);
    + return vfs_readlink(dentry,buffer,buflen,tmp);
    }

    static struct dentry * proc_self_follow_link(struct dentry *dentry,
    @@ -42,24 +37,13 @@
    unsigned int follow)
    {
    char tmp[30];
    -
    sprintf(tmp, "%d", current->pid);
    - return lookup_dentry(tmp, base, follow);
    + return vfs_follow_link(dentry,base,follow,tmp);
    }

    static struct inode_operations proc_self_inode_operations = {
    - NULL, /* no file-ops */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - proc_self_readlink, /* readlink */
    - proc_self_follow_link, /* follow_link */
    + readlink: proc_self_readlink,
    + follow_link: proc_self_follow_link
    };

    static struct proc_dir_entry proc_root_self = {
    diff -urN linux-2.3.32-pre2/fs/romfs/inode.c linux-bird.symlink/fs/romfs/inode.c
    --- linux-2.3.32-pre2/fs/romfs/inode.c Mon Dec 6 22:06:04 1999
    +++ linux-bird.symlink/fs/romfs/inode.c Wed Dec 8 20:19:27 1999
    @@ -444,63 +444,6 @@
    return result;
    }

    -static int
    -romfs_readlink(struct dentry *dentry, char *buffer, int len)
    -{
    - struct inode *inode = dentry->d_inode;
    - int mylen;
    - char buf[ROMFS_MAXFN]; /* XXX dynamic */
    -
    - if (!inode || !S_ISLNK(inode->i_mode)) {
    - mylen = -EBADF;
    - goto out;
    - }
    -
    - mylen = min(sizeof(buf), inode->i_size);
    -
    - if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, mylen) <= 0) {
    - mylen = -EIO;
    - goto out;
    - }
    - copy_to_user(buffer, buf, mylen);
    -
    -out:
    - return mylen;
    -}
    -
    -static struct dentry *romfs_follow_link(struct dentry *dentry,
    - struct dentry *base,
    - unsigned int follow)
    -{
    - struct inode *inode = dentry->d_inode;
    - char *link;
    - int len, cnt;
    -
    - len = inode->i_size;
    -
    - dentry = ERR_PTR(-EAGAIN); /* correct? */
    - if (!(link = kmalloc(len+1, GFP_KERNEL)))
    - goto outnobuf;
    -
    - cnt = romfs_copyfrom(inode, link, inode->u.romfs_i.i_dataoffset, len);
    - if (len != cnt) {
    - dentry = ERR_PTR(-EIO);
    - goto out;
    - } else
    - link[len] = 0;
    -
    - dentry = lookup_dentry(link, base, follow);
    - kfree(link);
    -
    - if (0) {
    -out:
    - kfree(link);
    -outnobuf:
    - dput(base);
    - }
    - return dentry;
    -}
    -
    /* Mapping from our types to the kernel */

    static struct file_operations romfs_file_operations = {
    @@ -584,24 +527,9 @@
    };

    static struct inode_operations romfs_link_inode_operations = {
    - NULL, /* no file operations on symlinks */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - romfs_readlink, /* readlink */
    - romfs_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    + readlink: page_readlink,
    + follow_link: page_follow_link,
    + readpage: romfs_readpage
    };

    static mode_t romfs_modemap[] =
    diff -urN linux-2.3.32-pre2/fs/sysv/namei.c linux-bird.symlink/fs/sysv/namei.c
    --- linux-2.3.32-pre2/fs/sysv/namei.c Sun Nov 7 15:23:06 1999
    +++ linux-bird.symlink/fs/sysv/namei.c Wed Dec 8 20:19:27 1999
    @@ -440,59 +440,41 @@
    int sysv_symlink(struct inode * dir, struct dentry * dentry,
    const char * symname)
    {
    - struct sysv_dir_entry * de;
    struct inode * inode;
    - struct buffer_head * name_block;
    - char * name_block_data;
    - struct super_block * sb;
    - int i;
    - char c;
    + struct sysv_dir_entry * de;
    struct buffer_head * bh;
    + int err;
    + int l;

    + err = -ENAMETOOLONG;
    + l = strlen(symname)+1;
    + if (l > dir->i_sb->sv_block_size_1)
    + goto out;
    + err = -ENOSPC;
    if (!(inode = sysv_new_inode(dir)))
    - return -ENOSPC;
    + goto out;

    inode->i_mode = S_IFLNK | 0777;
    inode->i_op = &sysv_symlink_inode_operations;
    - name_block = sysv_file_bread(inode, 0, 1);
    - if (!name_block) {
    - inode->i_nlink--;
    - mark_inode_dirty(inode);
    - iput(inode);
    - return -ENOSPC;
    - }
    - sb = inode->i_sb;
    - name_block_data = name_block->b_data;
    - i = 0;
    - while (i < sb->sv_block_size_1 && (c = *(symname++)))
    - name_block_data[i++] = c;
    - name_block_data[i] = 0;
    - mark_buffer_dirty(name_block, 1);
    - brelse(name_block);
    - inode->i_size = i;
    + err = block_symlink(inode, symname, l);
    + if (err)
    + goto out_no_entry;
    mark_inode_dirty(inode);
    - bh = sysv_find_entry(dir, dentry->d_name.name,
    - dentry->d_name.len, &de);
    - if (bh) {
    - inode->i_nlink--;
    - mark_inode_dirty(inode);
    - iput(inode);
    - brelse(bh);
    - return -EEXIST;
    - }
    - i = sysv_add_entry(dir, dentry->d_name.name,
    + err = sysv_add_entry(dir, dentry->d_name.name,
    dentry->d_name.len, &bh, &de);
    - if (i) {
    - inode->i_nlink--;
    - mark_inode_dirty(inode);
    - iput(inode);
    - return i;
    - }
    + if (err)
    + goto out_no_entry;
    de->inode = inode->i_ino;
    mark_buffer_dirty(bh, 1);
    brelse(bh);
    d_instantiate(dentry, inode);
    - return 0;
    +out:
    + return err;
    +out_no_entry:
    + inode->i_nlink--;
    + mark_inode_dirty(inode);
    + iput(inode);
    + goto out;
    }

    int sysv_link(struct dentry * old_dentry, struct inode * dir,
    diff -urN linux-2.3.32-pre2/fs/sysv/symlink.c linux-bird.symlink/fs/sysv/symlink.c
    --- linux-2.3.32-pre2/fs/sysv/symlink.c Mon Dec 6 22:06:04 1999
    +++ linux-bird.symlink/fs/sysv/symlink.c Wed Dec 8 20:19:27 1999
    @@ -13,77 +13,14 @@
    * SystemV/Coherent symlink handling code
    */

    -#include <linux/errno.h>
    -#include <linux/sched.h>
    #include <linux/sysv_fs.h>
    -#include <linux/stat.h>
    -
    -#include <asm/uaccess.h>
    -
    -static int sysv_readlink(struct dentry *, char *, int);
    -static struct dentry *sysv_follow_link(struct dentry *, struct dentry *, unsigned int);

    /*
    * symlinks can't do much...
    */
    struct inode_operations sysv_symlink_inode_operations = {
    - NULL, /* no file-operations */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - sysv_readlink, /* readlink */
    - sysv_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    + readlink: page_readlink,
    + follow_link: page_follow_link,
    + get_block: sysv_get_block,
    + readpage: block_read_full_page
    };
    -
    -static struct dentry *sysv_follow_link(struct dentry * dentry,
    - struct dentry * base,
    - unsigned int follow)
    -{
    - struct inode *inode = dentry->d_inode;
    - struct buffer_head * bh;
    -
    - bh = sysv_file_bread(inode, 0, 0);
    - if (!bh) {
    - dput(base);
    - return ERR_PTR(-EIO);
    - }
    - UPDATE_ATIME(inode);
    - base = lookup_dentry(bh->b_data, base, follow);
    - brelse(bh);
    - return base;
    -}
    -
    -static int sysv_readlink(struct dentry * dentry, char * buffer, int buflen)
    -{
    - struct inode *inode = dentry->d_inode;
    - struct buffer_head * bh;
    - char * bh_data;
    - int i;
    - char c;
    -
    - if (buflen > inode->i_sb->sv_block_size_1)
    - buflen = inode->i_sb->sv_block_size_1;
    - bh = sysv_file_bread(inode, 0, 0);
    - if (!bh)
    - return 0;
    - bh_data = bh->b_data;
    - i = 0;
    - while (i<buflen && (c = bh_data[i])) {
    - i++;
    - put_user(c,buffer++);
    - }
    - brelse(bh);
    - return i;
    -}
    diff -urN linux-2.3.32-pre2/fs/udf/symlink.c linux-bird.symlink/fs/udf/symlink.c
    --- linux-2.3.32-pre2/fs/udf/symlink.c Mon Dec 6 22:06:04 1999
    +++ linux-bird.symlink/fs/udf/symlink.c Wed Dec 8 20:19:27 1999
    @@ -33,155 +33,93 @@
    #include <linux/mm.h>
    #include <linux/stat.h>
    #include <linux/malloc.h>
    +#include <linux/pagemap.h>
    #include "udf_i.h"

    -static int udf_readlink(struct dentry *, char *, int);
    -static struct dentry * udf_follow_link(struct dentry * dentry,
    - struct dentry * base, unsigned int follow);
    -
    -/*
    - * symlinks can't do much...
    - */
    -struct inode_operations udf_symlink_inode_operations = {
    - NULL, /* no file-operations */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - udf_readlink, /* readlink */
    - udf_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    -};
    -
    -int udf_pc_to_char(char *from, int fromlen, char **to)
    +static void udf_pc_to_char(char *from, int fromlen, char *to)
    {
    struct PathComponent *pc;
    int elen = 0, len = 0;
    + char *p = to;

    - *to = (char *)kmalloc(fromlen, GFP_KERNEL);
    -
    - if (!(*to))
    - return -1;
    -
    - while (elen < fromlen)
    - {
    + while (elen < fromlen) {
    pc = (struct PathComponent *)(from + elen);
    - if (pc->componentType == 1 && pc->lengthComponentIdent == 0)
    - {
    - (*to)[0] = '/';
    - len = 1;
    - }
    - else if (pc->componentType == 3)
    - {
    - memcpy(&(*to)[len], "../", 3);
    - len += 3;
    - }
    - else if (pc->componentType == 4)
    - {
    - memcpy(&(*to)[len], "./", 2);
    - len += 2;
    - }
    - else if (pc->componentType == 5)
    - {
    - memcpy(&(*to)[len], pc->componentIdent, pc->lengthComponentIdent);
    - len += pc->lengthComponentIdent + 1;
    - (*to)[len-1] = '/';
    + switch (pc->componentType) {
    + case 1:
    + if (pc->lengthComponentIdent == 0) {
    + p = to;
    + *p++ = '/';
    + }
    + break;
    + case 3:
    + memcpy(p, "../", 3);
    + p += 3;
    + break;
    + case 4:
    + memcpy(p, "./", 2);
    + p += 2;
    + /* that would be . - just ignore */
    + break;
    + case 5:
    + memcpy(p+len, pc->componentIdent,
    + pc->lengthComponentIdent);
    + p += pc->lengthComponentIdent;
    + *p++ = '/';
    }
    elen += sizeof(struct PathComponent) + pc->lengthComponentIdent;
    }

    - if (len)
    - {
    - len --;
    - (*to)[len] = '\0';
    + if (p>to+1) {
    + p[-1] = '\0';
    }
    - return len;
    }

    -static struct dentry * udf_follow_link(struct dentry * dentry,
    - struct dentry * base, unsigned int follow)
    +static int udf_symlink_filler(struct dentry * dentry, struct page *page)
    {
    struct inode *inode = dentry->d_inode;
    struct buffer_head *bh = NULL;
    - char *symlink, *tmpbuf;
    - int len;
    -
    - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB)
    - {
    - bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize);
    -
    - if (!bh)
    - return 0;
    -
    - symlink = bh->b_data + udf_file_entry_alloc_offset(inode);
    - }
    - else
    - {
    - bh = bread(inode->i_dev, udf_block_map(inode, 0), inode->i_sb->s_blocksize);
    -
    - if (!bh)
    - return 0;
    -
    - symlink = bh->b_data;
    - }
    -
    - if ((len = udf_pc_to_char(symlink, inode->i_size, &tmpbuf)) >= 0)
    - {
    - base = lookup_dentry(tmpbuf, base, follow);
    - kfree(tmpbuf);
    - return base;
    - }
    - else
    - return ERR_PTR(-ENOMEM);
    -}
    + char *symlink;
    + int err;

    -static int udf_readlink(struct dentry * dentry, char * buffer, int buflen)
    -{
    - struct inode *inode = dentry->d_inode;
    - struct buffer_head *bh = NULL;
    - char *symlink, *tmpbuf;
    - int len;
    + char *p = (char*)kmap(page);

    - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB)
    - {
    - bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize);
    + err = -EIO;
    + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) {
    + bh = udf_tread(inode->i_sb, inode->i_ino,
    + inode->i_sb->s_blocksize);

    if (!bh)
    - return 0;
    + goto out;

    symlink = bh->b_data + udf_file_entry_alloc_offset(inode);
    - }
    - else
    - {
    - bh = bread(inode->i_dev, udf_block_map(inode, 0), inode->i_sb->s_blocksize);
    + } else {
    + bh = bread(inode->i_dev, udf_block_map(inode, 0),
    + inode->i_sb->s_blocksize);

    if (!bh)
    - return 0;
    + goto out;

    symlink = bh->b_data;
    }

    - if ((len = udf_pc_to_char(symlink, inode->i_size, &tmpbuf)) >= 0)
    - {
    - if (copy_to_user(buffer, tmpbuf, len > buflen ? buflen : len))
    - len = -EFAULT;
    - kfree(tmpbuf);
    - }
    - else
    - len = -ENOMEM;
    -
    - UPDATE_ATIME(inode);
    - if (bh)
    - udf_release_data(bh);
    - return len;
    + udf_pc_to_char(symlink, inode->i_size, p);
    + udf_release_data(bh);
    + SetPageUptodate(page);
    + kunmap(page);
    + UnlockPage(page);
    + return 0;
    +out:
    + SetPageError(page);
    + kunmap(page);
    + UnlockPage(page);
    + return -EIO;
    }
    +
    +/*
    + * symlinks can't do much...
    + */
    +struct inode_operations udf_symlink_inode_operations = {
    + readlink: page_readlink,
    + follow_link: page_follow_link,
    + readpage: udf_symlink_filler,
    +};
    diff -urN linux-2.3.32-pre2/fs/ufs/inode.c linux-bird.symlink/fs/ufs/inode.c
    --- linux-2.3.32-pre2/fs/ufs/inode.c Thu Nov 11 22:08:02 1999
    +++ linux-bird.symlink/fs/ufs/inode.c Wed Dec 8 20:19:27 1999
    @@ -631,7 +631,9 @@
    else if (S_ISDIR(inode->i_mode))
    inode->i_op = &ufs_dir_inode_operations;
    else if (S_ISLNK(inode->i_mode))
    - inode->i_op = &ufs_symlink_inode_operations;
    + inode->i_op = inode->i_blocks
    + ?&ufs_symlink_inode_operations
    + :&ufs_fast_symlink_inode_operations;
    else
    init_special_inode(inode, inode->i_mode,
    SWAB32(ufs_inode->ui_u2.ui_addr.ui_db[0]));
    diff -urN linux-2.3.32-pre2/fs/ufs/namei.c linux-bird.symlink/fs/ufs/namei.c
    --- linux-2.3.32-pre2/fs/ufs/namei.c Sun Sep 12 14:29:24 1999
    +++ linux-bird.symlink/fs/ufs/namei.c Wed Dec 8 20:19:27 1999
    @@ -751,55 +751,42 @@
    int ufs_symlink (struct inode * dir, struct dentry * dentry,
    const char * symname)
    {
    - struct super_block * sb;
    + struct super_block * sb = dir->i_sb;
    struct ufs_dir_entry * de;
    struct inode * inode;
    - struct buffer_head * bh, * name_block;
    - char * link;
    - unsigned i, l;
    + struct buffer_head * bh = NULL;
    + unsigned l;
    int err;
    - char c;
    - unsigned swab;
    + unsigned swab = sb->u.ufs_sb.s_swab;

    UFSD(("ENTER\n"))

    - sb = dir->i_sb;
    - swab = sb->u.ufs_sb.s_swab;
    - bh = name_block = NULL;
    +
    + err = -ENAMETOOLONG;
    + l = strlen(symname)+1;
    + if (l > dir->i_sb->s_blocksize)
    + goto out;
    +
    err = -EIO;

    if (!(inode = ufs_new_inode (dir, S_IFLNK, &err))) {
    return err;
    }
    inode->i_mode = S_IFLNK | S_IRWXUGO;
    - inode->i_op = &ufs_symlink_inode_operations;
    - for (l = 0; l < sb->s_blocksize - 1 && symname [l]; l++);

    - /***if (l >= sizeof (inode->u.ufs_i.i_data)) {***/
    + /***if (l > sizeof (inode->u.ufs_i.i_data)) {***/
    if (1) {
    /* slow symlink */
    - name_block = ufs_bread (inode, 0, 1, &err);
    - if (!name_block) {
    - inode->i_nlink--;
    - mark_inode_dirty(inode);
    - iput (inode);
    - return err;
    - }
    - link = name_block->b_data;
    -
    + inode->i_op = &ufs_symlink_inode_operations;
    + err = block_symlink(inode, symname, l);
    + if (err)
    + goto out_no_entry;
    } else {
    /* fast symlink */
    - link = (char *) inode->u.ufs_i.i_u1.i_data;
    + inode->i_op = &ufs_fast_symlink_inode_operations;
    + memcpy((char*)&inode->u.ufs_i.i_u1.i_data,symname,l);
    + inode->i_size = l-1;
    }
    - i = 0;
    - while (i < sb->s_blocksize - 1 && (c = *(symname++)))
    - link[i++] = c;
    - link[i] = 0;
    - if (name_block) {
    - mark_buffer_dirty(name_block, 1);
    - brelse (name_block);
    - }
    - inode->i_size = i;
    mark_inode_dirty(inode);

    bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
    @@ -828,16 +815,12 @@
    int ufs_link (struct dentry * old_dentry, struct inode * dir,
    struct dentry *dentry)
    {
    - struct super_block * sb;
    struct inode *inode = old_dentry->d_inode;
    + struct super_block * sb = inode->i_sb;
    struct ufs_dir_entry * de;
    struct buffer_head * bh;
    int err;
    - unsigned swab;
    -
    - inode = old_dentry->d_inode;
    - sb = inode->i_sb;
    - swab = sb->u.ufs_sb.s_swab;
    + unsigned swab = sb->u.ufs_sb.s_swab;

    if (S_ISDIR(inode->i_mode))
    return -EPERM;
    diff -urN linux-2.3.32-pre2/fs/ufs/symlink.c linux-bird.symlink/fs/ufs/symlink.c
    --- linux-2.3.32-pre2/fs/ufs/symlink.c Mon Dec 6 22:06:04 1999
    +++ linux-bird.symlink/fs/ufs/symlink.c Wed Dec 8 20:19:27 1999
    @@ -23,116 +23,29 @@
    * ext2 symlink handling code
    */

    -#include <asm/uaccess.h>
    -
    -#include <linux/errno.h>
    #include <linux/fs.h>
    #include <linux/ufs_fs.h>
    -#include <linux/sched.h>
    -#include <linux/mm.h>
    -#include <linux/stat.h>
    -
    -
    -#undef UFS_SYMLINK_DEBUG
    -
    -#ifdef UFS_SYMLINK_DEBUG
    -#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x;
    -#else
    -#define UFSD(x)
    -#endif
    -

    -static struct dentry * ufs_follow_link(struct dentry * dentry,
    - struct dentry * base, unsigned int follow)
    +static int ufs_readlink(struct dentry *dentry, char *buffer, int buflen)
    {
    - struct inode * inode;
    - struct buffer_head * bh;
    - int error;
    - char * link;
    -
    - UFSD(("ENTER\n"))
    -
    - inode = dentry->d_inode;
    - bh = NULL;
    - /* slow symlink */
    - if (inode->i_blocks) {
    - if (!(bh = ufs_bread (inode, 0, 0, &error))) {
    - dput(base);
    - return ERR_PTR(-EIO);
    - }
    - link = bh->b_data;
    - }
    - /* fast symlink */
    - else {
    - link = (char *) inode->u.ufs_i.i_u1.i_symlink;
    - }
    - UPDATE_ATIME(inode);
    - base = lookup_dentry(link, base, follow);
    - if (bh)
    - brelse(bh);
    - UFSD(("EXIT\n"))
    - return base;
    + char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink;
    + return vfs_readlink(dentry, buffer, buflen, s);
    }

    -static int ufs_readlink (struct dentry * dentry, char * buffer, int buflen)
    +static struct dentry *ufs_follow_link(struct dentry *dentry, struct dentry *base, unsigned flags)
    {
    - struct super_block * sb;
    - struct inode * inode;
    - struct buffer_head * bh;
    - char * link;
    - int i;
    -
    - UFSD(("ENTER\n"))
    -
    - inode = dentry->d_inode;
    - sb = inode->i_sb;
    - bh = NULL;
    - if (buflen > sb->s_blocksize - 1)
    - buflen = sb->s_blocksize - 1;
    - /* slow symlink */
    - if (inode->i_blocks) {
    - int err;
    - bh = ufs_bread (inode, 0, 0, &err);
    - if (!bh) {
    - if(err < 0) /* indicate type of error */
    - return err;
    - return 0;
    - }
    - link = bh->b_data;
    - }
    - /* fast symlink */
    - else {
    - link = (char *) inode->u.ufs_i.i_u1.i_symlink;
    - }
    - i = 0;
    - while (i < buflen && link[i])
    - i++;
    - if (copy_to_user(buffer, link, i))
    - i = -EFAULT;
    - UPDATE_ATIME(inode);
    - if (bh)
    - brelse (bh);
    - UFSD(("ENTER\n"))
    - return i;
    + char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink;
    + return vfs_follow_link(dentry, base, flags, s);
    }

    +struct inode_operations ufs_fast_symlink_inode_operations = {
    + readlink: ufs_readlink,
    + follow_link: ufs_follow_link,
    +};
    +
    struct inode_operations ufs_symlink_inode_operations = {
    - NULL, /* no file-operations */
    - NULL, /* create */
    - NULL, /* lookup */
    - NULL, /* link */
    - NULL, /* unlink */
    - NULL, /* symlink */
    - NULL, /* mkdir */
    - NULL, /* rmdir */
    - NULL, /* mknod */
    - NULL, /* rename */
    - ufs_readlink, /* readlink */
    - ufs_follow_link, /* follow_link */
    - NULL, /* get_block */
    - NULL, /* readpage */
    - NULL, /* writepage */
    - NULL, /* truncate */
    - NULL, /* permission */
    - NULL /* revalidate */
    + readlink: page_readlink,
    + follow_link: page_follow_link,
    + get_block: ufs_getfrag_block,
    + readpage: block_read_full_page
    };
    diff -urN linux-2.3.32-pre2/fs/umsdos/namei.c linux-bird.symlink/fs/umsdos/namei.c
    --- linux-2.3.32-pre2/fs/umsdos/namei.c Sun Sep 12 13:55:24 1999
    +++ linux-bird.symlink/fs/umsdos/namei.c Wed Dec 8 20:19:27 1999
    @@ -475,8 +475,6 @@
    * Let's go for simplicity...
    */

    -extern struct inode_operations umsdos_symlink_inode_operations;
    -
    /*
    * AV. Should be called with dir->i_sem down.
    */
    diff -urN linux-2.3.32-pre2/include/linux/ext2_fs.h linux-bird.symlink/include/linux/ext2_fs.h
    --- linux-2.3.32-pre2/include/linux/ext2_fs.h Sun Sep 12 16:55:57 1999
    +++ linux-bird.symlink/include/linux/ext2_fs.h Wed Dec 8 20:19:27 1999
    @@ -615,6 +615,7 @@

    /* symlink.c */
    extern struct inode_operations ext2_symlink_inode_operations;
    +extern struct inode_operations ext2_fast_symlink_inode_operations;

    #endif /* __KERNEL__ */

    diff -urN linux-2.3.32-pre2/include/linux/fs.h linux-bird.symlink/include/linux/fs.h
    --- linux-2.3.32-pre2/include/linux/fs.h Mon Dec 6 22:06:05 1999
    +++ linux-bird.symlink/include/linux/fs.h Wed Dec 8 20:19:27 1999
    @@ -946,12 +946,24 @@
    extern int block_write_full_page (struct dentry *, struct page *);
    extern int block_write_partial_page (struct file *, struct page *, unsigned long, unsigned long, const char *);
    extern int block_write_cont_page (struct file *, struct page *, unsigned long, unsigned long, const char *);
    +extern int block_write_zero_range(struct inode *, struct page *, unsigned, unsigned, unsigned, const char *);
    +extern inline int block_write_range(struct inode *inode, struct page *page,
    + unsigned from, unsigned len,const char *buf)
    +{
    + return block_write_zero_range(inode, page, from, from, from+len, buf);
    +}
    extern int block_flushpage(struct page *, unsigned long);
    +extern int block_symlink(struct inode *, const char *, int);

    extern int generic_file_mmap(struct file *, struct vm_area_struct *);
    extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
    extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *, writepage_t);
    -extern void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc, read_actor_t actor);
    +extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t);
    +
    +extern int vfs_readlink(struct dentry *, char *, int, char *);
    +extern struct dentry *vfs_follow_link(struct dentry *, struct dentry *, unsigned, char *);
    +extern int page_readlink(struct dentry *, char *, int);
    +extern struct dentry *page_follow_link(struct dentry *, struct dentry *, unsigned);

    extern struct super_block *get_super(kdev_t);
    struct super_block *get_empty_super(void);
    diff -urN linux-2.3.32-pre2/include/linux/ufs_fs.h linux-bird.symlink/include/linux/ufs_fs.h
    --- linux-2.3.32-pre2/include/linux/ufs_fs.h Mon Nov 22 08:50:18 1999
    +++ linux-bird.symlink/include/linux/ufs_fs.h Wed Dec 8 20:19:27 1999
    @@ -563,6 +563,7 @@

    /* symlink.c */
    extern struct inode_operations ufs_symlink_inode_operations;
    +extern struct inode_operations ufs_fast_symlink_inode_operations;

    /* truncate.c */
    extern void ufs_truncate (struct inode *);
    diff -urN linux-2.3.32-pre2/kernel/ksyms.c linux-bird.symlink/kernel/ksyms.c
    --- linux-2.3.32-pre2/kernel/ksyms.c Mon Dec 6 22:06:06 1999
    +++ linux-bird.symlink/kernel/ksyms.c Wed Dec 8 20:19:27 1999
    @@ -188,6 +188,7 @@
    EXPORT_SYMBOL(block_write_full_page);
    EXPORT_SYMBOL(block_write_partial_page);
    EXPORT_SYMBOL(block_write_cont_page);
    +EXPORT_SYMBOL(block_write_zero_range);
    EXPORT_SYMBOL(generic_file_read);
    EXPORT_SYMBOL(do_generic_file_read);
    EXPORT_SYMBOL(generic_file_write);
    @@ -218,6 +219,11 @@
    EXPORT_SYMBOL(__find_lock_page);
    EXPORT_SYMBOL(grab_cache_page);
    EXPORT_SYMBOL(read_cache_page);
    +EXPORT_SYMBOL(vfs_readlink);
    +EXPORT_SYMBOL(vfs_follow_link);
    +EXPORT_SYMBOL(page_readlink);
    +EXPORT_SYMBOL(page_follow_link);
    +EXPORT_SYMBOL(block_symlink);

    #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
    EXPORT_SYMBOL(do_nfsservctl);
    \
     
     \ /
      Last update: 2005-03-22 13:55    [W:0.129 / U:31.340 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site