X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fnfs%2Fsymlink.c;h=35f1065991441c3f3d02f6749692da739b8492b3;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=fabadfbfd47ea43890cfb30df05b48ec16174564;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index fabadfbfd..35f106599 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -23,20 +23,30 @@ #include #include #include +#include /* Symlink caching in the page cache is even more simplistic * and straight-forward than readdir caching. + * + * At the beginning of the page we store pointer to struct page in question, + * simplifying nfs_put_link() (if inode got invalidated we can't find the page + * to be freed via pagecache lookup). + * The NUL-terminated string follows immediately thereafter. */ + +struct nfs_symlink { + struct page *page; + char body[0]; +}; + static int nfs_symlink_filler(struct inode *inode, struct page *page) { + const unsigned int pgbase = offsetof(struct nfs_symlink, body); + const unsigned int pglen = PAGE_SIZE - pgbase; int error; - /* We place the length at the beginning of the page, - * in host byte order, followed by the string. The - * XDR response verification will NULL terminate it. - */ lock_kernel(); - error = NFS_PROTO(inode)->readlink(inode, page); + error = NFS_PROTO(inode)->readlink(inode, page, pgbase, pglen); unlock_kernel(); if (error < 0) goto error; @@ -50,61 +60,58 @@ error: return -EIO; } -static char *nfs_getlink(struct inode *inode, struct page **ppage) +static int nfs_follow_link(struct dentry *dentry, struct nameidata *nd) { + struct inode *inode = dentry->d_inode; struct page *page; - u32 *p; - - page = ERR_PTR(nfs_revalidate_inode(NFS_SERVER(inode), inode)); - if (page) + struct nfs_symlink *p; + void *err = ERR_PTR(nfs_revalidate_inode(NFS_SERVER(inode), inode)); + if (err) goto read_failed; page = read_cache_page(&inode->i_data, 0, (filler_t *)nfs_symlink_filler, inode); - if (IS_ERR(page)) + if (IS_ERR(page)) { + err = page; goto read_failed; - if (!PageUptodate(page)) + } + if (!PageUptodate(page)) { + err = ERR_PTR(-EIO); goto getlink_read_error; - *ppage = page; + } p = kmap(page); - return (char*)(p+1); + p->page = page; + nd_set_link(nd, p->body); + return 0; getlink_read_error: page_cache_release(page); - page = ERR_PTR(-EIO); read_failed: - return (char*)page; + nd_set_link(nd, err); + return 0; } -static int nfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) +static void nfs_put_link(struct dentry *dentry, struct nameidata *nd) { - struct inode *inode = dentry->d_inode; - struct page *page = NULL; - int res = vfs_readlink(dentry,buffer,buflen,nfs_getlink(inode,&page)); - if (page) { - kunmap(page); - page_cache_release(page); - } - return res; -} + char *s = nd_get_link(nd); + if (!IS_ERR(s)) { + struct nfs_symlink *p; + struct page *page; + + p = container_of(s, struct nfs_symlink, body[0]); + page = p->page; -static int nfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct inode *inode = dentry->d_inode; - struct page *page = NULL; - int res = vfs_follow_link(nd, nfs_getlink(inode,&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, + .readlink = generic_readlink, .follow_link = nfs_follow_link, + .put_link = nfs_put_link, .getattr = nfs_getattr, .setattr = nfs_setattr, };