Fix nasty ncpfs symlink handling bug.

This bug could cause oopses and page state corruption, because ncpfs
used the generic page-cache symlink handlign functions. But those
functions only work if the page cache is guaranteed to be "stable", ie a
page that was installed when the symlink walk was started has to still
be installed in the page cache at the end of the walk.

We could have fixed ncpfs to not use the generic helper routines, but it
is in many ways much cleaner to instead improve on the symlink walking
helper routines so that they don't require that absolute stability.

We do this by allowing "follow_link()" to return a error-pointer as a
cookie, which is fed back to the cleanup "put_link()" routine. This
also simplifies NFS symlink handling.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>

+54 -77
+3 -2
fs/autofs/symlink.c
··· 12 12 13 13 #include "autofs_i.h" 14 14 15 - static int autofs_follow_link(struct dentry *dentry, struct nameidata *nd) 15 + /* Nothing to release.. */ 16 + static void *autofs_follow_link(struct dentry *dentry, struct nameidata *nd) 16 17 { 17 18 char *s=((struct autofs_symlink *)dentry->d_inode->u.generic_ip)->data; 18 19 nd_set_link(nd, s); 19 - return 0; 20 + return NULL; 20 21 } 21 22 22 23 struct inode_operations autofs_symlink_inode_operations = {
+2 -2
fs/cifs/cifsfs.h
··· 83 83 extern struct dentry_operations cifs_dentry_ops; 84 84 85 85 /* Functions related to symlinks */ 86 - extern int cifs_follow_link(struct dentry *direntry, struct nameidata *nd); 87 - extern void cifs_put_link(struct dentry *direntry, struct nameidata *nd); 86 + extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); 87 + extern void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *); 88 88 extern int cifs_readlink(struct dentry *direntry, char __user *buffer, 89 89 int buflen); 90 90 extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
+3 -3
fs/cifs/link.c
··· 92 92 return rc; 93 93 } 94 94 95 - int 95 + void * 96 96 cifs_follow_link(struct dentry *direntry, struct nameidata *nd) 97 97 { 98 98 struct inode *inode = direntry->d_inode; ··· 148 148 out_no_free: 149 149 FreeXid(xid); 150 150 nd_set_link(nd, target_path); 151 - return 0; 151 + return NULL; /* No cookie */ 152 152 } 153 153 154 154 int ··· 330 330 return rc; 331 331 } 332 332 333 - void cifs_put_link(struct dentry *direntry, struct nameidata *nd) 333 + void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie) 334 334 { 335 335 char *p = nd_get_link(nd); 336 336 if (!IS_ERR(p))
+2 -2
fs/ext2/symlink.c
··· 21 21 #include "xattr.h" 22 22 #include <linux/namei.h> 23 23 24 - static int ext2_follow_link(struct dentry *dentry, struct nameidata *nd) 24 + static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd) 25 25 { 26 26 struct ext2_inode_info *ei = EXT2_I(dentry->d_inode); 27 27 nd_set_link(nd, (char *)ei->i_data); 28 - return 0; 28 + return NULL; 29 29 } 30 30 31 31 struct inode_operations ext2_symlink_inode_operations = {
+2 -2
fs/ext3/symlink.c
··· 23 23 #include <linux/namei.h> 24 24 #include "xattr.h" 25 25 26 - static int ext3_follow_link(struct dentry *dentry, struct nameidata *nd) 26 + static void * ext3_follow_link(struct dentry *dentry, struct nameidata *nd) 27 27 { 28 28 struct ext3_inode_info *ei = EXT3_I(dentry->d_inode); 29 29 nd_set_link(nd, (char*)ei->i_data); 30 - return 0; 30 + return NULL; 31 31 } 32 32 33 33 struct inode_operations ext3_symlink_inode_operations = {
+21 -19
fs/namei.c
··· 501 501 static inline int __do_follow_link(struct path *path, struct nameidata *nd) 502 502 { 503 503 int error; 504 + void *cookie; 504 505 struct dentry *dentry = path->dentry; 505 506 506 507 touch_atime(path->mnt, dentry); ··· 509 508 510 509 if (path->mnt == nd->mnt) 511 510 mntget(path->mnt); 512 - error = dentry->d_inode->i_op->follow_link(dentry, nd); 513 - if (!error) { 511 + cookie = dentry->d_inode->i_op->follow_link(dentry, nd); 512 + error = PTR_ERR(cookie); 513 + if (!IS_ERR(cookie)) { 514 514 char *s = nd_get_link(nd); 515 + error = 0; 515 516 if (s) 516 517 error = __vfs_follow_link(nd, s); 517 518 if (dentry->d_inode->i_op->put_link) 518 - dentry->d_inode->i_op->put_link(dentry, nd); 519 + dentry->d_inode->i_op->put_link(dentry, nd, cookie); 519 520 } 520 521 dput(dentry); 521 522 mntput(path->mnt); ··· 2347 2344 int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) 2348 2345 { 2349 2346 struct nameidata nd; 2350 - int res; 2347 + void *cookie; 2348 + 2351 2349 nd.depth = 0; 2352 - res = dentry->d_inode->i_op->follow_link(dentry, &nd); 2353 - if (!res) { 2354 - res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd)); 2350 + cookie = dentry->d_inode->i_op->follow_link(dentry, &nd); 2351 + if (!IS_ERR(cookie)) { 2352 + int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd)); 2355 2353 if (dentry->d_inode->i_op->put_link) 2356 - dentry->d_inode->i_op->put_link(dentry, &nd); 2354 + dentry->d_inode->i_op->put_link(dentry, &nd, cookie); 2355 + cookie = ERR_PTR(res); 2357 2356 } 2358 - return res; 2357 + return PTR_ERR(cookie); 2359 2358 } 2360 2359 2361 2360 int vfs_follow_link(struct nameidata *nd, const char *link) ··· 2400 2395 return res; 2401 2396 } 2402 2397 2403 - int page_follow_link_light(struct dentry *dentry, struct nameidata *nd) 2398 + void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd) 2404 2399 { 2405 - struct page *page; 2400 + struct page *page = NULL; 2406 2401 nd_set_link(nd, page_getlink(dentry, &page)); 2407 - return 0; 2402 + return page; 2408 2403 } 2409 2404 2410 - void page_put_link(struct dentry *dentry, struct nameidata *nd) 2405 + void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) 2411 2406 { 2412 - if (!IS_ERR(nd_get_link(nd))) { 2413 - struct page *page; 2414 - page = find_get_page(dentry->d_inode->i_mapping, 0); 2415 - if (!page) 2416 - BUG(); 2407 + struct page *page = cookie; 2408 + 2409 + if (page) { 2417 2410 kunmap(page); 2418 - page_cache_release(page); 2419 2411 page_cache_release(page); 2420 2412 } 2421 2413 }
+8 -29
fs/nfs/symlink.c
··· 27 27 28 28 /* Symlink caching in the page cache is even more simplistic 29 29 * and straight-forward than readdir caching. 30 - * 31 - * At the beginning of the page we store pointer to struct page in question, 32 - * simplifying nfs_put_link() (if inode got invalidated we can't find the page 33 - * to be freed via pagecache lookup). 34 - * The NUL-terminated string follows immediately thereafter. 35 30 */ 36 - 37 - struct nfs_symlink { 38 - struct page *page; 39 - char body[0]; 40 - }; 41 31 42 32 static int nfs_symlink_filler(struct inode *inode, struct page *page) 43 33 { 44 - const unsigned int pgbase = offsetof(struct nfs_symlink, body); 45 - const unsigned int pglen = PAGE_SIZE - pgbase; 46 34 int error; 47 35 48 36 lock_kernel(); 49 - error = NFS_PROTO(inode)->readlink(inode, page, pgbase, pglen); 37 + error = NFS_PROTO(inode)->readlink(inode, page, 0, PAGE_SIZE); 50 38 unlock_kernel(); 51 39 if (error < 0) 52 40 goto error; ··· 48 60 return -EIO; 49 61 } 50 62 51 - static int nfs_follow_link(struct dentry *dentry, struct nameidata *nd) 63 + static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) 52 64 { 53 65 struct inode *inode = dentry->d_inode; 54 66 struct page *page; 55 - struct nfs_symlink *p; 56 67 void *err = ERR_PTR(nfs_revalidate_inode(NFS_SERVER(inode), inode)); 57 68 if (err) 58 69 goto read_failed; ··· 65 78 err = ERR_PTR(-EIO); 66 79 goto getlink_read_error; 67 80 } 68 - p = kmap(page); 69 - p->page = page; 70 - nd_set_link(nd, p->body); 71 - return 0; 81 + nd_set_link(nd, kmap(page)); 82 + return page; 72 83 73 84 getlink_read_error: 74 85 page_cache_release(page); 75 86 read_failed: 76 87 nd_set_link(nd, err); 77 - return 0; 88 + return NULL; 78 89 } 79 90 80 - static void nfs_put_link(struct dentry *dentry, struct nameidata *nd) 91 + static void nfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) 81 92 { 82 - char *s = nd_get_link(nd); 83 - if (!IS_ERR(s)) { 84 - struct nfs_symlink *p; 85 - struct page *page; 86 - 87 - p = container_of(s, struct nfs_symlink, body[0]); 88 - page = p->page; 89 - 93 + if (cookie) { 94 + struct page *page = cookie; 90 95 kunmap(page); 91 96 page_cache_release(page); 92 97 }
+3 -3
fs/sysfs/symlink.c
··· 151 151 152 152 } 153 153 154 - static int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) 154 + static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) 155 155 { 156 156 int error = -ENOMEM; 157 157 unsigned long page = get_zeroed_page(GFP_KERNEL); 158 158 if (page) 159 159 error = sysfs_getlink(dentry, (char *) page); 160 160 nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); 161 - return 0; 161 + return NULL; 162 162 } 163 163 164 - static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd) 164 + static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) 165 165 { 166 166 char *page = nd_get_link(nd); 167 167 if (!IS_ERR(page))
+4 -4
include/linux/fs.h
··· 993 993 int (*rename) (struct inode *, struct dentry *, 994 994 struct inode *, struct dentry *); 995 995 int (*readlink) (struct dentry *, char __user *,int); 996 - int (*follow_link) (struct dentry *, struct nameidata *); 997 - void (*put_link) (struct dentry *, struct nameidata *); 996 + void * (*follow_link) (struct dentry *, struct nameidata *); 997 + void (*put_link) (struct dentry *, struct nameidata *, void *); 998 998 void (*truncate) (struct inode *); 999 999 int (*permission) (struct inode *, int, struct nameidata *); 1000 1000 int (*setattr) (struct dentry *, struct iattr *); ··· 1602 1602 extern int vfs_readlink(struct dentry *, char __user *, int, const char *); 1603 1603 extern int vfs_follow_link(struct nameidata *, const char *); 1604 1604 extern int page_readlink(struct dentry *, char __user *, int); 1605 - extern int page_follow_link_light(struct dentry *, struct nameidata *); 1606 - extern void page_put_link(struct dentry *, struct nameidata *); 1605 + extern void *page_follow_link_light(struct dentry *, struct nameidata *); 1606 + extern void page_put_link(struct dentry *, struct nameidata *, void *); 1607 1607 extern int page_symlink(struct inode *inode, const char *symname, int len); 1608 1608 extern struct inode_operations page_symlink_inode_operations; 1609 1609 extern int generic_readlink(struct dentry *, char __user *, int);
+6 -11
mm/shmem.c
··· 1773 1773 return 0; 1774 1774 } 1775 1775 1776 - static int shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd) 1776 + static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd) 1777 1777 { 1778 1778 nd_set_link(nd, (char *)SHMEM_I(dentry->d_inode)); 1779 - return 0; 1779 + return NULL; 1780 1780 } 1781 1781 1782 - static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd) 1782 + static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd) 1783 1783 { 1784 1784 struct page *page = NULL; 1785 1785 int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL); 1786 1786 nd_set_link(nd, res ? ERR_PTR(res) : kmap(page)); 1787 - return 0; 1787 + return page; 1788 1788 } 1789 1789 1790 - static void shmem_put_link(struct dentry *dentry, struct nameidata *nd) 1790 + static void shmem_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) 1791 1791 { 1792 1792 if (!IS_ERR(nd_get_link(nd))) { 1793 - struct page *page; 1794 - 1795 - page = find_get_page(dentry->d_inode->i_mapping, 0); 1796 - if (!page) 1797 - BUG(); 1793 + struct page *page = cookie; 1798 1794 kunmap(page); 1799 1795 mark_page_accessed(page); 1800 - page_cache_release(page); 1801 1796 page_cache_release(page); 1802 1797 } 1803 1798 }