diff options
-rw-r--r-- | fs/autofs/symlink.c | 5 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 4 | ||||
-rw-r--r-- | fs/cifs/link.c | 6 | ||||
-rw-r--r-- | fs/ext2/symlink.c | 4 | ||||
-rw-r--r-- | fs/ext3/symlink.c | 4 | ||||
-rw-r--r-- | fs/namei.c | 40 | ||||
-rw-r--r-- | fs/nfs/symlink.c | 37 | ||||
-rw-r--r-- | fs/sysfs/symlink.c | 6 | ||||
-rw-r--r-- | include/linux/fs.h | 8 | ||||
-rw-r--r-- | mm/shmem.c | 17 |
10 files changed, 54 insertions, 77 deletions
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c index f028396f1383..52e8772b066e 100644 --- a/fs/autofs/symlink.c +++ b/fs/autofs/symlink.c | |||
@@ -12,11 +12,12 @@ | |||
12 | 12 | ||
13 | #include "autofs_i.h" | 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 | 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 | ||
22 | struct inode_operations autofs_symlink_inode_operations = { | 23 | struct inode_operations autofs_symlink_inode_operations = { |
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 78af5850c558..1fd21f66f243 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h | |||
@@ -83,8 +83,8 @@ extern int cifs_dir_notify(struct file *, unsigned long arg); | |||
83 | extern struct dentry_operations cifs_dentry_ops; | 83 | extern struct dentry_operations cifs_dentry_ops; |
84 | 84 | ||
85 | /* Functions related to symlinks */ | 85 | /* Functions related to symlinks */ |
86 | extern int cifs_follow_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); | 87 | extern void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *); |
88 | extern int cifs_readlink(struct dentry *direntry, char __user *buffer, | 88 | extern int cifs_readlink(struct dentry *direntry, char __user *buffer, |
89 | int buflen); | 89 | int buflen); |
90 | extern int cifs_symlink(struct inode *inode, struct dentry *direntry, | 90 | extern int cifs_symlink(struct inode *inode, struct dentry *direntry, |
diff --git a/fs/cifs/link.c b/fs/cifs/link.c index bde0fabfece0..ab925ef4f863 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c | |||
@@ -92,7 +92,7 @@ cifs_hl_exit: | |||
92 | return rc; | 92 | return rc; |
93 | } | 93 | } |
94 | 94 | ||
95 | int | 95 | void * |
96 | cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | 96 | cifs_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: | |||
148 | out_no_free: | 148 | out_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 | ||
154 | int | 154 | int |
@@ -330,7 +330,7 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) | |||
330 | return rc; | 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 | char *p = nd_get_link(nd); | 335 | char *p = nd_get_link(nd); |
336 | if (!IS_ERR(p)) | 336 | if (!IS_ERR(p)) |
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 9f7bac01d557..1e67d87cfa91 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c | |||
@@ -21,11 +21,11 @@ | |||
21 | #include "xattr.h" | 21 | #include "xattr.h" |
22 | #include <linux/namei.h> | 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 | 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 | ||
31 | struct inode_operations ext2_symlink_inode_operations = { | 31 | struct inode_operations ext2_symlink_inode_operations = { |
diff --git a/fs/ext3/symlink.c b/fs/ext3/symlink.c index 8c3e72818fb0..4f79122cde67 100644 --- a/fs/ext3/symlink.c +++ b/fs/ext3/symlink.c | |||
@@ -23,11 +23,11 @@ | |||
23 | #include <linux/namei.h> | 23 | #include <linux/namei.h> |
24 | #include "xattr.h" | 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 | 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 | ||
33 | struct inode_operations ext3_symlink_inode_operations = { | 33 | struct inode_operations ext3_symlink_inode_operations = { |
diff --git a/fs/namei.c b/fs/namei.c index b85f158aef0c..6ec1f0fefc5b 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -501,6 +501,7 @@ struct path { | |||
501 | static inline int __do_follow_link(struct path *path, struct nameidata *nd) | 501 | static 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) | |||
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: | |||
2344 | int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) | 2347 | int 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 | ||
2358 | int vfs_follow_link(struct nameidata *nd, const char *link) | 2363 | int vfs_follow_link(struct nameidata *nd, const char *link) |
@@ -2395,23 +2400,20 @@ int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) | |||
2395 | return res; | 2400 | return res; |
2396 | } | 2401 | } |
2397 | 2402 | ||
2398 | int page_follow_link_light(struct dentry *dentry, struct nameidata *nd) | 2403 | void *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 | ||
2405 | void page_put_link(struct dentry *dentry, struct nameidata *nd) | 2410 | void 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 | ||
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 35f106599144..18dc95b0b646 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c | |||
@@ -27,26 +27,14 @@ | |||
27 | 27 | ||
28 | /* Symlink caching in the page cache is even more simplistic | 28 | /* 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 | ||
37 | struct nfs_symlink { | ||
38 | struct page *page; | ||
39 | char body[0]; | ||
40 | }; | ||
41 | |||
42 | static int nfs_symlink_filler(struct inode *inode, struct page *page) | 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 | 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: | |||
60 | return -EIO; | 48 | return -EIO; |
61 | } | 49 | } |
62 | 50 | ||
63 | static int nfs_follow_link(struct dentry *dentry, struct nameidata *nd) | 51 | static 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) | |||
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 | ||
86 | getlink_read_error: | 71 | getlink_read_error: |
87 | page_cache_release(page); | 72 | page_cache_release(page); |
88 | read_failed: | 73 | read_failed: |
89 | nd_set_link(nd, err); | 74 | nd_set_link(nd, err); |
90 | return 0; | 75 | return NULL; |
91 | } | 76 | } |
92 | 77 | ||
93 | static void nfs_put_link(struct dentry *dentry, struct nameidata *nd) | 78 | static 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 | } |
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index fae57c83a722..de402fa915f2 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c | |||
@@ -151,17 +151,17 @@ static int sysfs_getlink(struct dentry *dentry, char * path) | |||
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 | 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 | ||
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 | char *page = nd_get_link(nd); | 166 | char *page = nd_get_link(nd); |
167 | if (!IS_ERR(page)) | 167 | if (!IS_ERR(page)) |
diff --git a/include/linux/fs.h b/include/linux/fs.h index f9adf75fd9b4..67e6732d4fdc 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -993,8 +993,8 @@ struct inode_operations { | |||
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; | |||
1602 | extern int vfs_readlink(struct dentry *, char __user *, int, const char *); | 1602 | extern int vfs_readlink(struct dentry *, char __user *, int, const char *); |
1603 | extern int vfs_follow_link(struct nameidata *, const char *); | 1603 | extern int vfs_follow_link(struct nameidata *, const char *); |
1604 | extern int page_readlink(struct dentry *, char __user *, int); | 1604 | extern int page_readlink(struct dentry *, char __user *, int); |
1605 | extern int page_follow_link_light(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 *); | 1606 | extern void page_put_link(struct dentry *, struct nameidata *, void *); |
1607 | extern int page_symlink(struct inode *inode, const char *symname, int len); | 1607 | extern int page_symlink(struct inode *inode, const char *symname, int len); |
1608 | extern struct inode_operations page_symlink_inode_operations; | 1608 | extern struct inode_operations page_symlink_inode_operations; |
1609 | extern int generic_readlink(struct dentry *, char __user *, int); | 1609 | extern int generic_readlink(struct dentry *, char __user *, int); |
diff --git a/mm/shmem.c b/mm/shmem.c index e64fa726a790..5a81b1ee4f7a 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -1773,32 +1773,27 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s | |||
1773 | return 0; | 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 | 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 | ||
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 | 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 | ||
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 | 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 | ||