History

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.

Linus Torvalds committed 21 years ago

Commit cc314ee1 parent 2fb1e3010 files · +54 −77Browse files

Unified · Split

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