diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2007-12-01 08:33:17 -0500 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2007-12-01 08:33:17 -0500 |
commit | 2b1e300a9dfc3196ccddf6f1d74b91b7af55e416 (patch) | |
tree | 3181bf4f2c27d185c78b26642f35ed00cb280943 | |
parent | e03ba84adb62fbc6049325a5bc00ef6932fa5e39 (diff) |
[NETNS]: Fix /proc/net breakage
Well I clearly goofed when I added the initial network namespace support
for /proc/net. Currently things work but there are odd details visible to
user space, even when we have a single network namespace.
Since we do not cache proc_dir_entry dentries at the moment we can just
modify ->lookup to return a different directory inode depending on the
network namespace of the process looking at /proc/net, replacing the
current technique of using a magic and fragile follow_link method.
To accomplish that this patch:
- introduces a shadow_proc method to allow different dentries to
be returned from proc_lookup.
- Removes the old /proc/net follow_link magic
- Fixes a weakness in our not caching of proc generic dentries.
As shadow_proc uses a task struct to decided which dentry to return we can
go back later and fix the proc generic caching without modifying any code
that uses the shadow_proc method.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
-rw-r--r-- | fs/proc/generic.c | 12 | ||||
-rw-r--r-- | fs/proc/proc_net.c | 86 | ||||
-rw-r--r-- | include/linux/proc_fs.h | 3 |
3 files changed, 19 insertions, 82 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index a9806bc21ec3..c2b752341f89 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -374,9 +374,16 @@ static int proc_delete_dentry(struct dentry * dentry) | |||
374 | return 1; | 374 | return 1; |
375 | } | 375 | } |
376 | 376 | ||
377 | static int proc_revalidate_dentry(struct dentry *dentry, struct nameidata *nd) | ||
378 | { | ||
379 | d_drop(dentry); | ||
380 | return 0; | ||
381 | } | ||
382 | |||
377 | static struct dentry_operations proc_dentry_operations = | 383 | static struct dentry_operations proc_dentry_operations = |
378 | { | 384 | { |
379 | .d_delete = proc_delete_dentry, | 385 | .d_delete = proc_delete_dentry, |
386 | .d_revalidate = proc_revalidate_dentry, | ||
380 | }; | 387 | }; |
381 | 388 | ||
382 | /* | 389 | /* |
@@ -397,8 +404,11 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam | |||
397 | if (de->namelen != dentry->d_name.len) | 404 | if (de->namelen != dentry->d_name.len) |
398 | continue; | 405 | continue; |
399 | if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { | 406 | if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { |
400 | unsigned int ino = de->low_ino; | 407 | unsigned int ino; |
401 | 408 | ||
409 | if (de->shadow_proc) | ||
410 | de = de->shadow_proc(current, de); | ||
411 | ino = de->low_ino; | ||
402 | de_get(de); | 412 | de_get(de); |
403 | spin_unlock(&proc_subdir_lock); | 413 | spin_unlock(&proc_subdir_lock); |
404 | error = -EINVAL; | 414 | error = -EINVAL; |
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 131f9c68be5f..0afe21ee0607 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c | |||
@@ -50,89 +50,14 @@ struct net *get_proc_net(const struct inode *inode) | |||
50 | } | 50 | } |
51 | EXPORT_SYMBOL_GPL(get_proc_net); | 51 | EXPORT_SYMBOL_GPL(get_proc_net); |
52 | 52 | ||
53 | static struct proc_dir_entry *proc_net_shadow; | 53 | static struct proc_dir_entry *shadow_pde; |
54 | 54 | ||
55 | static struct dentry *proc_net_shadow_dentry(struct dentry *parent, | 55 | static struct proc_dir_entry *proc_net_shadow(struct task_struct *task, |
56 | struct proc_dir_entry *de) | 56 | struct proc_dir_entry *de) |
57 | { | 57 | { |
58 | struct dentry *shadow = NULL; | 58 | return task->nsproxy->net_ns->proc_net; |
59 | struct inode *inode; | ||
60 | if (!de) | ||
61 | goto out; | ||
62 | de_get(de); | ||
63 | inode = proc_get_inode(parent->d_inode->i_sb, de->low_ino, de); | ||
64 | if (!inode) | ||
65 | goto out_de_put; | ||
66 | shadow = d_alloc_name(parent, de->name); | ||
67 | if (!shadow) | ||
68 | goto out_iput; | ||
69 | shadow->d_op = parent->d_op; /* proc_dentry_operations */ | ||
70 | d_instantiate(shadow, inode); | ||
71 | out: | ||
72 | return shadow; | ||
73 | out_iput: | ||
74 | iput(inode); | ||
75 | out_de_put: | ||
76 | de_put(de); | ||
77 | goto out; | ||
78 | } | ||
79 | |||
80 | static void *proc_net_follow_link(struct dentry *parent, struct nameidata *nd) | ||
81 | { | ||
82 | struct net *net = current->nsproxy->net_ns; | ||
83 | struct dentry *shadow; | ||
84 | shadow = proc_net_shadow_dentry(parent, net->proc_net); | ||
85 | if (!shadow) | ||
86 | return ERR_PTR(-ENOENT); | ||
87 | |||
88 | dput(nd->dentry); | ||
89 | /* My dentry count is 1 and that should be enough as the | ||
90 | * shadow dentry is thrown away immediately. | ||
91 | */ | ||
92 | nd->dentry = shadow; | ||
93 | return NULL; | ||
94 | } | 59 | } |
95 | 60 | ||
96 | static struct dentry *proc_net_lookup(struct inode *dir, struct dentry *dentry, | ||
97 | struct nameidata *nd) | ||
98 | { | ||
99 | struct net *net = current->nsproxy->net_ns; | ||
100 | struct dentry *shadow; | ||
101 | |||
102 | shadow = proc_net_shadow_dentry(nd->dentry, net->proc_net); | ||
103 | if (!shadow) | ||
104 | return ERR_PTR(-ENOENT); | ||
105 | |||
106 | dput(nd->dentry); | ||
107 | nd->dentry = shadow; | ||
108 | |||
109 | return shadow->d_inode->i_op->lookup(shadow->d_inode, dentry, nd); | ||
110 | } | ||
111 | |||
112 | static int proc_net_setattr(struct dentry *dentry, struct iattr *iattr) | ||
113 | { | ||
114 | struct net *net = current->nsproxy->net_ns; | ||
115 | struct dentry *shadow; | ||
116 | int ret; | ||
117 | |||
118 | shadow = proc_net_shadow_dentry(dentry->d_parent, net->proc_net); | ||
119 | if (!shadow) | ||
120 | return -ENOENT; | ||
121 | ret = shadow->d_inode->i_op->setattr(shadow, iattr); | ||
122 | dput(shadow); | ||
123 | return ret; | ||
124 | } | ||
125 | |||
126 | static const struct file_operations proc_net_dir_operations = { | ||
127 | .read = generic_read_dir, | ||
128 | }; | ||
129 | |||
130 | static struct inode_operations proc_net_dir_inode_operations = { | ||
131 | .follow_link = proc_net_follow_link, | ||
132 | .lookup = proc_net_lookup, | ||
133 | .setattr = proc_net_setattr, | ||
134 | }; | ||
135 | |||
136 | static __net_init int proc_net_ns_init(struct net *net) | 61 | static __net_init int proc_net_ns_init(struct net *net) |
137 | { | 62 | { |
138 | struct proc_dir_entry *root, *netd, *net_statd; | 63 | struct proc_dir_entry *root, *netd, *net_statd; |
@@ -185,9 +110,8 @@ static struct pernet_operations __net_initdata proc_net_ns_ops = { | |||
185 | 110 | ||
186 | int __init proc_net_init(void) | 111 | int __init proc_net_init(void) |
187 | { | 112 | { |
188 | proc_net_shadow = proc_mkdir("net", NULL); | 113 | shadow_pde = proc_mkdir("net", NULL); |
189 | proc_net_shadow->proc_iops = &proc_net_dir_inode_operations; | 114 | shadow_pde->shadow_proc = proc_net_shadow; |
190 | proc_net_shadow->proc_fops = &proc_net_dir_operations; | ||
191 | 115 | ||
192 | return register_pernet_subsys(&proc_net_ns_ops); | 116 | return register_pernet_subsys(&proc_net_ns_ops); |
193 | } | 117 | } |
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 1273c6ec535c..523528d237b0 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h | |||
@@ -48,6 +48,8 @@ typedef int (read_proc_t)(char *page, char **start, off_t off, | |||
48 | typedef int (write_proc_t)(struct file *file, const char __user *buffer, | 48 | typedef int (write_proc_t)(struct file *file, const char __user *buffer, |
49 | unsigned long count, void *data); | 49 | unsigned long count, void *data); |
50 | typedef int (get_info_t)(char *, char **, off_t, int); | 50 | typedef int (get_info_t)(char *, char **, off_t, int); |
51 | typedef struct proc_dir_entry *(shadow_proc_t)(struct task_struct *task, | ||
52 | struct proc_dir_entry *pde); | ||
51 | 53 | ||
52 | struct proc_dir_entry { | 54 | struct proc_dir_entry { |
53 | unsigned int low_ino; | 55 | unsigned int low_ino; |
@@ -79,6 +81,7 @@ struct proc_dir_entry { | |||
79 | int pde_users; /* number of callers into module in progress */ | 81 | int pde_users; /* number of callers into module in progress */ |
80 | spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ | 82 | spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ |
81 | struct completion *pde_unload_completion; | 83 | struct completion *pde_unload_completion; |
84 | shadow_proc_t *shadow_proc; | ||
82 | }; | 85 | }; |
83 | 86 | ||
84 | struct kcore_list { | 87 | struct kcore_list { |