aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2011-06-27 16:25:29 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2011-07-20 01:44:09 -0400
commit2780f1ff6aec0cf708a61c022d475bfcaa648965 (patch)
treec8c65497d2ab7c56bde6fc6166489d9caf9386e9 /drivers/base
parent6657719390cd05be45f4e3b501d8bb46889c0a19 (diff)
switch devtmpfs object creation/removal to separate kernel thread
... and give it a namespace where devtmpfs would be mounted on root, thus avoiding abuses of vfs_path_lookup() (it was never intended to be used with LOOKUP_PARENT). Games with credentials are also gone. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/devtmpfs.c222
1 files changed, 149 insertions, 73 deletions
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 82bbb5967aa9..1a16e1fd7f8a 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -21,12 +21,11 @@
21#include <linux/fs.h> 21#include <linux/fs.h>
22#include <linux/shmem_fs.h> 22#include <linux/shmem_fs.h>
23#include <linux/ramfs.h> 23#include <linux/ramfs.h>
24#include <linux/cred.h>
25#include <linux/sched.h> 24#include <linux/sched.h>
26#include <linux/init_task.h>
27#include <linux/slab.h> 25#include <linux/slab.h>
26#include <linux/kthread.h>
28 27
29static struct vfsmount *dev_mnt; 28static struct task_struct *thread;
30 29
31#if defined CONFIG_DEVTMPFS_MOUNT 30#if defined CONFIG_DEVTMPFS_MOUNT
32static int mount_dev = 1; 31static int mount_dev = 1;
@@ -34,7 +33,16 @@ static int mount_dev = 1;
34static int mount_dev; 33static int mount_dev;
35#endif 34#endif
36 35
37static DEFINE_MUTEX(dirlock); 36static DEFINE_SPINLOCK(req_lock);
37
38static struct req {
39 struct req *next;
40 struct completion done;
41 int err;
42 const char *name;
43 mode_t mode; /* 0 => delete */
44 struct device *dev;
45} *requests;
38 46
39static int __init mount_param(char *str) 47static int __init mount_param(char *str)
40{ 48{
@@ -68,14 +76,79 @@ static inline int is_blockdev(struct device *dev)
68static inline int is_blockdev(struct device *dev) { return 0; } 76static inline int is_blockdev(struct device *dev) { return 0; }
69#endif 77#endif
70 78
79int devtmpfs_create_node(struct device *dev)
80{
81 const char *tmp = NULL;
82 struct req req;
83
84 if (!thread)
85 return 0;
86
87 req.mode = 0;
88 req.name = device_get_devnode(dev, &req.mode, &tmp);
89 if (!req.name)
90 return -ENOMEM;
91
92 if (req.mode == 0)
93 req.mode = 0600;
94 if (is_blockdev(dev))
95 req.mode |= S_IFBLK;
96 else
97 req.mode |= S_IFCHR;
98
99 req.dev = dev;
100
101 init_completion(&req.done);
102
103 spin_lock(&req_lock);
104 req.next = requests;
105 requests = &req;
106 spin_unlock(&req_lock);
107
108 wake_up_process(thread);
109 wait_for_completion(&req.done);
110
111 kfree(tmp);
112
113 return req.err;
114}
115
116int devtmpfs_delete_node(struct device *dev)
117{
118 const char *tmp = NULL;
119 struct req req;
120
121 if (!thread)
122 return 0;
123
124 req.name = device_get_devnode(dev, NULL, &tmp);
125 if (!req.name)
126 return -ENOMEM;
127
128 req.mode = 0;
129 req.dev = dev;
130
131 init_completion(&req.done);
132
133 spin_lock(&req_lock);
134 req.next = requests;
135 requests = &req;
136 spin_unlock(&req_lock);
137
138 wake_up_process(thread);
139 wait_for_completion(&req.done);
140
141 kfree(tmp);
142 return req.err;
143}
144
71static int dev_mkdir(const char *name, mode_t mode) 145static int dev_mkdir(const char *name, mode_t mode)
72{ 146{
73 struct nameidata nd; 147 struct nameidata nd;
74 struct dentry *dentry; 148 struct dentry *dentry;
75 int err; 149 int err;
76 150
77 err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, 151 err = kern_path_parent(name, &nd);
78 name, LOOKUP_PARENT, &nd);
79 if (err) 152 if (err)
80 return err; 153 return err;
81 154
@@ -84,7 +157,7 @@ static int dev_mkdir(const char *name, mode_t mode)
84 err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode); 157 err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
85 if (!err) 158 if (!err)
86 /* mark as kernel-created inode */ 159 /* mark as kernel-created inode */
87 dentry->d_inode->i_private = &dev_mnt; 160 dentry->d_inode->i_private = &thread;
88 dput(dentry); 161 dput(dentry);
89 } else { 162 } else {
90 err = PTR_ERR(dentry); 163 err = PTR_ERR(dentry);
@@ -99,7 +172,6 @@ static int create_path(const char *nodepath)
99{ 172{
100 int err; 173 int err;
101 174
102 mutex_lock(&dirlock);
103 err = dev_mkdir(nodepath, 0755); 175 err = dev_mkdir(nodepath, 0755);
104 if (err == -ENOENT) { 176 if (err == -ENOENT) {
105 char *path; 177 char *path;
@@ -126,45 +198,22 @@ static int create_path(const char *nodepath)
126 kfree(path); 198 kfree(path);
127 } 199 }
128out: 200out:
129 mutex_unlock(&dirlock);
130 return err; 201 return err;
131} 202}
132 203
133int devtmpfs_create_node(struct device *dev) 204static int handle_create(const char *nodename, mode_t mode, struct device *dev)
134{ 205{
135 const char *tmp = NULL;
136 const char *nodename;
137 const struct cred *curr_cred;
138 mode_t mode = 0;
139 struct nameidata nd; 206 struct nameidata nd;
140 struct dentry *dentry; 207 struct dentry *dentry;
141 int err; 208 int err;
142 209
143 if (!dev_mnt) 210 err = kern_path_parent(nodename, &nd);
144 return 0;
145
146 nodename = device_get_devnode(dev, &mode, &tmp);
147 if (!nodename)
148 return -ENOMEM;
149
150 if (mode == 0)
151 mode = 0600;
152 if (is_blockdev(dev))
153 mode |= S_IFBLK;
154 else
155 mode |= S_IFCHR;
156
157 curr_cred = override_creds(&init_cred);
158
159 err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
160 nodename, LOOKUP_PARENT, &nd);
161 if (err == -ENOENT) { 211 if (err == -ENOENT) {
162 create_path(nodename); 212 create_path(nodename);
163 err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, 213 err = kern_path_parent(nodename, &nd);
164 nodename, LOOKUP_PARENT, &nd);
165 } 214 }
166 if (err) 215 if (err)
167 goto out; 216 return err;
168 217
169 dentry = lookup_create(&nd, 0); 218 dentry = lookup_create(&nd, 0);
170 if (!IS_ERR(dentry)) { 219 if (!IS_ERR(dentry)) {
@@ -181,7 +230,7 @@ int devtmpfs_create_node(struct device *dev)
181 mutex_unlock(&dentry->d_inode->i_mutex); 230 mutex_unlock(&dentry->d_inode->i_mutex);
182 231
183 /* mark as kernel-created inode */ 232 /* mark as kernel-created inode */
184 dentry->d_inode->i_private = &dev_mnt; 233 dentry->d_inode->i_private = &thread;
185 } 234 }
186 dput(dentry); 235 dput(dentry);
187 } else { 236 } else {
@@ -190,9 +239,6 @@ int devtmpfs_create_node(struct device *dev)
190 239
191 mutex_unlock(&nd.path.dentry->d_inode->i_mutex); 240 mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
192 path_put(&nd.path); 241 path_put(&nd.path);
193out:
194 kfree(tmp);
195 revert_creds(curr_cred);
196 return err; 242 return err;
197} 243}
198 244
@@ -202,8 +248,7 @@ static int dev_rmdir(const char *name)
202 struct dentry *dentry; 248 struct dentry *dentry;
203 int err; 249 int err;
204 250
205 err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, 251 err = kern_path_parent(name, &nd);
206 name, LOOKUP_PARENT, &nd);
207 if (err) 252 if (err)
208 return err; 253 return err;
209 254
@@ -211,7 +256,7 @@ static int dev_rmdir(const char *name)
211 dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); 256 dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
212 if (!IS_ERR(dentry)) { 257 if (!IS_ERR(dentry)) {
213 if (dentry->d_inode) { 258 if (dentry->d_inode) {
214 if (dentry->d_inode->i_private == &dev_mnt) 259 if (dentry->d_inode->i_private == &thread)
215 err = vfs_rmdir(nd.path.dentry->d_inode, 260 err = vfs_rmdir(nd.path.dentry->d_inode,
216 dentry); 261 dentry);
217 else 262 else
@@ -238,7 +283,6 @@ static int delete_path(const char *nodepath)
238 if (!path) 283 if (!path)
239 return -ENOMEM; 284 return -ENOMEM;
240 285
241 mutex_lock(&dirlock);
242 for (;;) { 286 for (;;) {
243 char *base; 287 char *base;
244 288
@@ -250,7 +294,6 @@ static int delete_path(const char *nodepath)
250 if (err) 294 if (err)
251 break; 295 break;
252 } 296 }
253 mutex_unlock(&dirlock);
254 297
255 kfree(path); 298 kfree(path);
256 return err; 299 return err;
@@ -259,7 +302,7 @@ static int delete_path(const char *nodepath)
259static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat) 302static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat)
260{ 303{
261 /* did we create it */ 304 /* did we create it */
262 if (inode->i_private != &dev_mnt) 305 if (inode->i_private != &thread)
263 return 0; 306 return 0;
264 307
265 /* does the dev_t match */ 308 /* does the dev_t match */
@@ -277,29 +320,17 @@ static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *sta
277 return 1; 320 return 1;
278} 321}
279 322
280int devtmpfs_delete_node(struct device *dev) 323static int handle_remove(const char *nodename, struct device *dev)
281{ 324{
282 const char *tmp = NULL;
283 const char *nodename;
284 const struct cred *curr_cred;
285 struct nameidata nd; 325 struct nameidata nd;
286 struct dentry *dentry; 326 struct dentry *dentry;
287 struct kstat stat; 327 struct kstat stat;
288 int deleted = 1; 328 int deleted = 1;
289 int err; 329 int err;
290 330
291 if (!dev_mnt) 331 err = kern_path_parent(nodename, &nd);
292 return 0;
293
294 nodename = device_get_devnode(dev, NULL, &tmp);
295 if (!nodename)
296 return -ENOMEM;
297
298 curr_cred = override_creds(&init_cred);
299 err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
300 nodename, LOOKUP_PARENT, &nd);
301 if (err) 332 if (err)
302 goto out; 333 return err;
303 334
304 mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); 335 mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
305 dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); 336 dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
@@ -337,9 +368,6 @@ int devtmpfs_delete_node(struct device *dev)
337 path_put(&nd.path); 368 path_put(&nd.path);
338 if (deleted && strchr(nodename, '/')) 369 if (deleted && strchr(nodename, '/'))
339 delete_path(nodename); 370 delete_path(nodename);
340out:
341 kfree(tmp);
342 revert_creds(curr_cred);
343 return err; 371 return err;
344} 372}
345 373
@@ -354,7 +382,7 @@ int devtmpfs_mount(const char *mntdir)
354 if (!mount_dev) 382 if (!mount_dev)
355 return 0; 383 return 0;
356 384
357 if (!dev_mnt) 385 if (!thread)
358 return 0; 386 return 0;
359 387
360 err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL); 388 err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL);
@@ -365,31 +393,79 @@ int devtmpfs_mount(const char *mntdir)
365 return err; 393 return err;
366} 394}
367 395
396static __initdata DECLARE_COMPLETION(setup_done);
397
398static int handle(const char *name, mode_t mode, struct device *dev)
399{
400 if (mode)
401 return handle_create(name, mode, dev);
402 else
403 return handle_remove(name, dev);
404}
405
406static int devtmpfsd(void *p)
407{
408 char options[] = "mode=0755";
409 int *err = p;
410 *err = sys_unshare(CLONE_NEWNS);
411 if (*err)
412 goto out;
413 *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options);
414 if (*err)
415 goto out;
416 sys_chdir("/.."); /* will traverse into overmounted root */
417 sys_chroot(".");
418 complete(&setup_done);
419 while (1) {
420 spin_lock(&req_lock);
421 while (requests) {
422 struct req *req = requests;
423 requests = NULL;
424 spin_unlock(&req_lock);
425 while (req) {
426 req->err = handle(req->name, req->mode, req->dev);
427 complete(&req->done);
428 req = req->next;
429 }
430 spin_lock(&req_lock);
431 }
432 set_current_state(TASK_INTERRUPTIBLE);
433 spin_unlock(&req_lock);
434 schedule();
435 __set_current_state(TASK_RUNNING);
436 }
437 return 0;
438out:
439 complete(&setup_done);
440 return *err;
441}
442
368/* 443/*
369 * Create devtmpfs instance, driver-core devices will add their device 444 * Create devtmpfs instance, driver-core devices will add their device
370 * nodes here. 445 * nodes here.
371 */ 446 */
372int __init devtmpfs_init(void) 447int __init devtmpfs_init(void)
373{ 448{
374 int err; 449 int err = register_filesystem(&dev_fs_type);
375 struct vfsmount *mnt;
376 char options[] = "mode=0755";
377
378 err = register_filesystem(&dev_fs_type);
379 if (err) { 450 if (err) {
380 printk(KERN_ERR "devtmpfs: unable to register devtmpfs " 451 printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
381 "type %i\n", err); 452 "type %i\n", err);
382 return err; 453 return err;
383 } 454 }
384 455
385 mnt = kern_mount_data(&dev_fs_type, options); 456 thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");
386 if (IS_ERR(mnt)) { 457 if (!IS_ERR(thread)) {
387 err = PTR_ERR(mnt); 458 wait_for_completion(&setup_done);
459 } else {
460 err = PTR_ERR(thread);
461 thread = NULL;
462 }
463
464 if (err) {
388 printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); 465 printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
389 unregister_filesystem(&dev_fs_type); 466 unregister_filesystem(&dev_fs_type);
390 return err; 467 return err;
391 } 468 }
392 dev_mnt = mnt;
393 469
394 printk(KERN_INFO "devtmpfs: initialized\n"); 470 printk(KERN_INFO "devtmpfs: initialized\n");
395 return 0; 471 return 0;