diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-09 16:55:51 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-09 16:55:51 -0500 |
commit | 78833dd70602be6b71ef34225f708b1e500947dc (patch) | |
tree | 807050f760ccc7305b194d7568ebf98737076a40 | |
parent | a5abba989deceb731047425812d268daf7536575 (diff) | |
parent | b306419ae08d9def53f2142a37cc0a58622307a8 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6:
nd->inode is not set on the second attempt in path_walk()
unfuck proc_sysctl ->d_compare()
minimal fix for do_filp_open() race
-rw-r--r-- | fs/namei.c | 14 | ||||
-rw-r--r-- | fs/proc/inode.c | 8 | ||||
-rw-r--r-- | fs/proc/proc_sysctl.c | 7 | ||||
-rw-r--r-- | include/linux/sysctl.h | 14 | ||||
-rw-r--r-- | kernel/sysctl.c | 15 |
5 files changed, 42 insertions, 16 deletions
diff --git a/fs/namei.c b/fs/namei.c index 0087cf9c2c6b..a4689eb2df28 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1546,6 +1546,7 @@ static int path_walk(const char *name, struct nameidata *nd) | |||
1546 | /* nd->path had been dropped */ | 1546 | /* nd->path had been dropped */ |
1547 | current->total_link_count = 0; | 1547 | current->total_link_count = 0; |
1548 | nd->path = save; | 1548 | nd->path = save; |
1549 | nd->inode = save.dentry->d_inode; | ||
1549 | path_get(&nd->path); | 1550 | path_get(&nd->path); |
1550 | nd->flags |= LOOKUP_REVAL; | 1551 | nd->flags |= LOOKUP_REVAL; |
1551 | result = link_path_walk(name, nd); | 1552 | result = link_path_walk(name, nd); |
@@ -2455,22 +2456,29 @@ struct file *do_filp_open(int dfd, const char *pathname, | |||
2455 | /* !O_CREAT, simple open */ | 2456 | /* !O_CREAT, simple open */ |
2456 | error = do_path_lookup(dfd, pathname, flags, &nd); | 2457 | error = do_path_lookup(dfd, pathname, flags, &nd); |
2457 | if (unlikely(error)) | 2458 | if (unlikely(error)) |
2458 | goto out_filp; | 2459 | goto out_filp2; |
2459 | error = -ELOOP; | 2460 | error = -ELOOP; |
2460 | if (!(nd.flags & LOOKUP_FOLLOW)) { | 2461 | if (!(nd.flags & LOOKUP_FOLLOW)) { |
2461 | if (nd.inode->i_op->follow_link) | 2462 | if (nd.inode->i_op->follow_link) |
2462 | goto out_path; | 2463 | goto out_path2; |
2463 | } | 2464 | } |
2464 | error = -ENOTDIR; | 2465 | error = -ENOTDIR; |
2465 | if (nd.flags & LOOKUP_DIRECTORY) { | 2466 | if (nd.flags & LOOKUP_DIRECTORY) { |
2466 | if (!nd.inode->i_op->lookup) | 2467 | if (!nd.inode->i_op->lookup) |
2467 | goto out_path; | 2468 | goto out_path2; |
2468 | } | 2469 | } |
2469 | audit_inode(pathname, nd.path.dentry); | 2470 | audit_inode(pathname, nd.path.dentry); |
2470 | filp = finish_open(&nd, open_flag, acc_mode); | 2471 | filp = finish_open(&nd, open_flag, acc_mode); |
2472 | out2: | ||
2471 | release_open_intent(&nd); | 2473 | release_open_intent(&nd); |
2472 | return filp; | 2474 | return filp; |
2473 | 2475 | ||
2476 | out_path2: | ||
2477 | path_put(&nd.path); | ||
2478 | out_filp2: | ||
2479 | filp = ERR_PTR(error); | ||
2480 | goto out2; | ||
2481 | |||
2474 | creat: | 2482 | creat: |
2475 | /* OK, have to create the file. Find the parent. */ | 2483 | /* OK, have to create the file. Find the parent. */ |
2476 | error = path_init_rcu(dfd, pathname, | 2484 | error = path_init_rcu(dfd, pathname, |
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 176ce4cda68a..d6a7ca1fdac5 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -27,6 +27,7 @@ | |||
27 | static void proc_evict_inode(struct inode *inode) | 27 | static void proc_evict_inode(struct inode *inode) |
28 | { | 28 | { |
29 | struct proc_dir_entry *de; | 29 | struct proc_dir_entry *de; |
30 | struct ctl_table_header *head; | ||
30 | 31 | ||
31 | truncate_inode_pages(&inode->i_data, 0); | 32 | truncate_inode_pages(&inode->i_data, 0); |
32 | end_writeback(inode); | 33 | end_writeback(inode); |
@@ -38,8 +39,11 @@ static void proc_evict_inode(struct inode *inode) | |||
38 | de = PROC_I(inode)->pde; | 39 | de = PROC_I(inode)->pde; |
39 | if (de) | 40 | if (de) |
40 | pde_put(de); | 41 | pde_put(de); |
41 | if (PROC_I(inode)->sysctl) | 42 | head = PROC_I(inode)->sysctl; |
42 | sysctl_head_put(PROC_I(inode)->sysctl); | 43 | if (head) { |
44 | rcu_assign_pointer(PROC_I(inode)->sysctl, NULL); | ||
45 | sysctl_head_put(head); | ||
46 | } | ||
43 | } | 47 | } |
44 | 48 | ||
45 | struct vfsmount *proc_mnt; | 49 | struct vfsmount *proc_mnt; |
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 09a1f92a34ef..8eb2522111c5 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
@@ -408,15 +408,18 @@ static int proc_sys_compare(const struct dentry *parent, | |||
408 | const struct dentry *dentry, const struct inode *inode, | 408 | const struct dentry *dentry, const struct inode *inode, |
409 | unsigned int len, const char *str, const struct qstr *name) | 409 | unsigned int len, const char *str, const struct qstr *name) |
410 | { | 410 | { |
411 | struct ctl_table_header *head; | ||
411 | /* Although proc doesn't have negative dentries, rcu-walk means | 412 | /* Although proc doesn't have negative dentries, rcu-walk means |
412 | * that inode here can be NULL */ | 413 | * that inode here can be NULL */ |
414 | /* AV: can it, indeed? */ | ||
413 | if (!inode) | 415 | if (!inode) |
414 | return 0; | 416 | return 1; |
415 | if (name->len != len) | 417 | if (name->len != len) |
416 | return 1; | 418 | return 1; |
417 | if (memcmp(name->name, str, len)) | 419 | if (memcmp(name->name, str, len)) |
418 | return 1; | 420 | return 1; |
419 | return !sysctl_is_seen(PROC_I(inode)->sysctl); | 421 | head = rcu_dereference(PROC_I(inode)->sysctl); |
422 | return !head || !sysctl_is_seen(head); | ||
420 | } | 423 | } |
421 | 424 | ||
422 | static const struct dentry_operations proc_sys_dentry_operations = { | 425 | static const struct dentry_operations proc_sys_dentry_operations = { |
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 7bb5cb64f3b8..bb7c2b086fa4 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
26 | #include <linux/types.h> | 26 | #include <linux/types.h> |
27 | #include <linux/compiler.h> | 27 | #include <linux/compiler.h> |
28 | #include <linux/rcupdate.h> | ||
28 | 29 | ||
29 | struct completion; | 30 | struct completion; |
30 | 31 | ||
@@ -1037,10 +1038,15 @@ struct ctl_table_root { | |||
1037 | struct ctl_table trees. */ | 1038 | struct ctl_table trees. */ |
1038 | struct ctl_table_header | 1039 | struct ctl_table_header |
1039 | { | 1040 | { |
1040 | struct ctl_table *ctl_table; | 1041 | union { |
1041 | struct list_head ctl_entry; | 1042 | struct { |
1042 | int used; | 1043 | struct ctl_table *ctl_table; |
1043 | int count; | 1044 | struct list_head ctl_entry; |
1045 | int used; | ||
1046 | int count; | ||
1047 | }; | ||
1048 | struct rcu_head rcu; | ||
1049 | }; | ||
1044 | struct completion *unregistering; | 1050 | struct completion *unregistering; |
1045 | struct ctl_table *ctl_table_arg; | 1051 | struct ctl_table *ctl_table_arg; |
1046 | struct ctl_table_root *root; | 1052 | struct ctl_table_root *root; |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 0f1bd83db985..4eed0af5d144 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -194,9 +194,9 @@ static int sysrq_sysctl_handler(ctl_table *table, int write, | |||
194 | static struct ctl_table root_table[]; | 194 | static struct ctl_table root_table[]; |
195 | static struct ctl_table_root sysctl_table_root; | 195 | static struct ctl_table_root sysctl_table_root; |
196 | static struct ctl_table_header root_table_header = { | 196 | static struct ctl_table_header root_table_header = { |
197 | .count = 1, | 197 | {{.count = 1, |
198 | .ctl_table = root_table, | 198 | .ctl_table = root_table, |
199 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list), | 199 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}}, |
200 | .root = &sysctl_table_root, | 200 | .root = &sysctl_table_root, |
201 | .set = &sysctl_table_root.default_set, | 201 | .set = &sysctl_table_root.default_set, |
202 | }; | 202 | }; |
@@ -1567,11 +1567,16 @@ void sysctl_head_get(struct ctl_table_header *head) | |||
1567 | spin_unlock(&sysctl_lock); | 1567 | spin_unlock(&sysctl_lock); |
1568 | } | 1568 | } |
1569 | 1569 | ||
1570 | static void free_head(struct rcu_head *rcu) | ||
1571 | { | ||
1572 | kfree(container_of(rcu, struct ctl_table_header, rcu)); | ||
1573 | } | ||
1574 | |||
1570 | void sysctl_head_put(struct ctl_table_header *head) | 1575 | void sysctl_head_put(struct ctl_table_header *head) |
1571 | { | 1576 | { |
1572 | spin_lock(&sysctl_lock); | 1577 | spin_lock(&sysctl_lock); |
1573 | if (!--head->count) | 1578 | if (!--head->count) |
1574 | kfree(head); | 1579 | call_rcu(&head->rcu, free_head); |
1575 | spin_unlock(&sysctl_lock); | 1580 | spin_unlock(&sysctl_lock); |
1576 | } | 1581 | } |
1577 | 1582 | ||
@@ -1948,10 +1953,10 @@ void unregister_sysctl_table(struct ctl_table_header * header) | |||
1948 | start_unregistering(header); | 1953 | start_unregistering(header); |
1949 | if (!--header->parent->count) { | 1954 | if (!--header->parent->count) { |
1950 | WARN_ON(1); | 1955 | WARN_ON(1); |
1951 | kfree(header->parent); | 1956 | call_rcu(&header->parent->rcu, free_head); |
1952 | } | 1957 | } |
1953 | if (!--header->count) | 1958 | if (!--header->count) |
1954 | kfree(header); | 1959 | call_rcu(&header->rcu, free_head); |
1955 | spin_unlock(&sysctl_lock); | 1960 | spin_unlock(&sysctl_lock); |
1956 | } | 1961 | } |
1957 | 1962 | ||