]>
Commit | Line | Data |
---|---|---|
2b2af54a KS |
1 | /* |
2 | * devtmpfs - kernel-maintained tmpfs-based /dev | |
3 | * | |
4 | * Copyright (C) 2009, Kay Sievers <[email protected]> | |
5 | * | |
6 | * During bootup, before any driver core device is registered, | |
7 | * devtmpfs, a tmpfs-based filesystem is created. Every driver-core | |
8 | * device which requests a device node, will add a node in this | |
e454cea2 KS |
9 | * filesystem. |
10 | * By default, all devices are named after the the name of the | |
11 | * device, owned by root and have a default mode of 0600. Subsystems | |
12 | * can overwrite the default setting if needed. | |
2b2af54a KS |
13 | */ |
14 | ||
15 | #include <linux/kernel.h> | |
16 | #include <linux/syscalls.h> | |
17 | #include <linux/mount.h> | |
18 | #include <linux/device.h> | |
19 | #include <linux/genhd.h> | |
20 | #include <linux/namei.h> | |
21 | #include <linux/fs.h> | |
22 | #include <linux/shmem_fs.h> | |
da5e4ef7 | 23 | #include <linux/ramfs.h> |
e454cea2 | 24 | #include <linux/sched.h> |
5a0e3ad6 | 25 | #include <linux/slab.h> |
2780f1ff | 26 | #include <linux/kthread.h> |
2b2af54a | 27 | |
2780f1ff | 28 | static struct task_struct *thread; |
2b2af54a KS |
29 | |
30 | #if defined CONFIG_DEVTMPFS_MOUNT | |
fc14f2fe | 31 | static int mount_dev = 1; |
2b2af54a | 32 | #else |
fc14f2fe | 33 | static int mount_dev; |
2b2af54a KS |
34 | #endif |
35 | ||
2780f1ff AV |
36 | static DEFINE_SPINLOCK(req_lock); |
37 | ||
38 | static struct req { | |
39 | struct req *next; | |
40 | struct completion done; | |
41 | int err; | |
42 | const char *name; | |
2c9ede55 | 43 | umode_t mode; /* 0 => delete */ |
2780f1ff AV |
44 | struct device *dev; |
45 | } *requests; | |
ed413ae6 | 46 | |
2b2af54a KS |
47 | static int __init mount_param(char *str) |
48 | { | |
fc14f2fe | 49 | mount_dev = simple_strtoul(str, NULL, 0); |
2b2af54a KS |
50 | return 1; |
51 | } | |
52 | __setup("devtmpfs.mount=", mount_param); | |
53 | ||
fc14f2fe AV |
54 | static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, |
55 | const char *dev_name, void *data) | |
2b2af54a | 56 | { |
da5e4ef7 | 57 | #ifdef CONFIG_TMPFS |
fc14f2fe | 58 | return mount_single(fs_type, flags, data, shmem_fill_super); |
da5e4ef7 | 59 | #else |
fc14f2fe | 60 | return mount_single(fs_type, flags, data, ramfs_fill_super); |
da5e4ef7 | 61 | #endif |
2b2af54a KS |
62 | } |
63 | ||
64 | static struct file_system_type dev_fs_type = { | |
65 | .name = "devtmpfs", | |
fc14f2fe | 66 | .mount = dev_mount, |
2b2af54a KS |
67 | .kill_sb = kill_litter_super, |
68 | }; | |
69 | ||
70 | #ifdef CONFIG_BLOCK | |
71 | static inline int is_blockdev(struct device *dev) | |
72 | { | |
73 | return dev->class == &block_class; | |
74 | } | |
75 | #else | |
76 | static inline int is_blockdev(struct device *dev) { return 0; } | |
77 | #endif | |
78 | ||
2780f1ff AV |
79 | int 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 | ||
116 | int 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 | ||
fbd48a69 | 145 | static int dev_mkdir(const char *name, umode_t mode) |
2b2af54a | 146 | { |
2b2af54a | 147 | struct dentry *dentry; |
69753a0f | 148 | struct path path; |
2b2af54a KS |
149 | int err; |
150 | ||
69753a0f AV |
151 | dentry = kern_path_create(AT_FDCWD, name, &path, 1); |
152 | if (IS_ERR(dentry)) | |
153 | return PTR_ERR(dentry); | |
154 | ||
155 | err = vfs_mkdir(path.dentry->d_inode, dentry, mode); | |
156 | if (!err) | |
157 | /* mark as kernel-created inode */ | |
158 | dentry->d_inode->i_private = &thread; | |
159 | dput(dentry); | |
160 | mutex_unlock(&path.dentry->d_inode->i_mutex); | |
161 | path_put(&path); | |
2b2af54a KS |
162 | return err; |
163 | } | |
164 | ||
165 | static int create_path(const char *nodepath) | |
166 | { | |
5da4e689 AV |
167 | char *path; |
168 | char *s; | |
9d108d25 | 169 | int err = 0; |
2b2af54a | 170 | |
5da4e689 AV |
171 | /* parent directories do not exist, create them */ |
172 | path = kstrdup(nodepath, GFP_KERNEL); | |
173 | if (!path) | |
174 | return -ENOMEM; | |
175 | ||
176 | s = path; | |
177 | for (;;) { | |
178 | s = strchr(s, '/'); | |
179 | if (!s) | |
180 | break; | |
181 | s[0] = '\0'; | |
182 | err = dev_mkdir(path, 0755); | |
183 | if (err && err != -EEXIST) | |
184 | break; | |
185 | s[0] = '/'; | |
186 | s++; | |
2b2af54a | 187 | } |
5da4e689 | 188 | kfree(path); |
2b2af54a KS |
189 | return err; |
190 | } | |
191 | ||
fbd48a69 | 192 | static int handle_create(const char *nodename, umode_t mode, struct device *dev) |
2b2af54a | 193 | { |
2b2af54a | 194 | struct dentry *dentry; |
69753a0f | 195 | struct path path; |
2b2af54a KS |
196 | int err; |
197 | ||
69753a0f AV |
198 | dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); |
199 | if (dentry == ERR_PTR(-ENOENT)) { | |
2b2af54a | 200 | create_path(nodename); |
69753a0f | 201 | dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); |
2b2af54a | 202 | } |
69753a0f AV |
203 | if (IS_ERR(dentry)) |
204 | return PTR_ERR(dentry); | |
205 | ||
206 | err = vfs_mknod(path.dentry->d_inode, | |
207 | dentry, mode, dev->devt); | |
208 | if (!err) { | |
209 | struct iattr newattrs; | |
210 | ||
211 | /* fixup possibly umasked mode */ | |
212 | newattrs.ia_mode = mode; | |
213 | newattrs.ia_valid = ATTR_MODE; | |
214 | mutex_lock(&dentry->d_inode->i_mutex); | |
215 | notify_change(dentry, &newattrs); | |
216 | mutex_unlock(&dentry->d_inode->i_mutex); | |
217 | ||
218 | /* mark as kernel-created inode */ | |
219 | dentry->d_inode->i_private = &thread; | |
2b2af54a | 220 | } |
69753a0f | 221 | dput(dentry); |
2b2af54a | 222 | |
69753a0f AV |
223 | mutex_unlock(&path.dentry->d_inode->i_mutex); |
224 | path_put(&path); | |
2b2af54a KS |
225 | return err; |
226 | } | |
227 | ||
228 | static int dev_rmdir(const char *name) | |
229 | { | |
230 | struct nameidata nd; | |
231 | struct dentry *dentry; | |
232 | int err; | |
233 | ||
2780f1ff | 234 | err = kern_path_parent(name, &nd); |
2b2af54a KS |
235 | if (err) |
236 | return err; | |
237 | ||
238 | mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); | |
239 | dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); | |
240 | if (!IS_ERR(dentry)) { | |
015bf43b | 241 | if (dentry->d_inode) { |
2780f1ff | 242 | if (dentry->d_inode->i_private == &thread) |
015bf43b KS |
243 | err = vfs_rmdir(nd.path.dentry->d_inode, |
244 | dentry); | |
245 | else | |
246 | err = -EPERM; | |
247 | } else { | |
2b2af54a | 248 | err = -ENOENT; |
015bf43b | 249 | } |
2b2af54a KS |
250 | dput(dentry); |
251 | } else { | |
252 | err = PTR_ERR(dentry); | |
253 | } | |
2b2af54a | 254 | |
015bf43b | 255 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); |
2b2af54a KS |
256 | path_put(&nd.path); |
257 | return err; | |
258 | } | |
259 | ||
260 | static int delete_path(const char *nodepath) | |
261 | { | |
262 | const char *path; | |
263 | int err = 0; | |
264 | ||
265 | path = kstrdup(nodepath, GFP_KERNEL); | |
266 | if (!path) | |
267 | return -ENOMEM; | |
268 | ||
ed413ae6 | 269 | for (;;) { |
2b2af54a KS |
270 | char *base; |
271 | ||
272 | base = strrchr(path, '/'); | |
273 | if (!base) | |
274 | break; | |
275 | base[0] = '\0'; | |
276 | err = dev_rmdir(path); | |
277 | if (err) | |
278 | break; | |
279 | } | |
280 | ||
281 | kfree(path); | |
282 | return err; | |
283 | } | |
284 | ||
285 | static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat) | |
286 | { | |
287 | /* did we create it */ | |
2780f1ff | 288 | if (inode->i_private != &thread) |
2b2af54a KS |
289 | return 0; |
290 | ||
291 | /* does the dev_t match */ | |
292 | if (is_blockdev(dev)) { | |
293 | if (!S_ISBLK(stat->mode)) | |
294 | return 0; | |
295 | } else { | |
296 | if (!S_ISCHR(stat->mode)) | |
297 | return 0; | |
298 | } | |
299 | if (stat->rdev != dev->devt) | |
300 | return 0; | |
301 | ||
302 | /* ours */ | |
303 | return 1; | |
304 | } | |
305 | ||
2780f1ff | 306 | static int handle_remove(const char *nodename, struct device *dev) |
2b2af54a | 307 | { |
2b2af54a KS |
308 | struct nameidata nd; |
309 | struct dentry *dentry; | |
310 | struct kstat stat; | |
311 | int deleted = 1; | |
312 | int err; | |
313 | ||
2780f1ff | 314 | err = kern_path_parent(nodename, &nd); |
2b2af54a | 315 | if (err) |
2780f1ff | 316 | return err; |
2b2af54a KS |
317 | |
318 | mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); | |
319 | dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len); | |
320 | if (!IS_ERR(dentry)) { | |
321 | if (dentry->d_inode) { | |
322 | err = vfs_getattr(nd.path.mnt, dentry, &stat); | |
323 | if (!err && dev_mynode(dev, dentry->d_inode, &stat)) { | |
5e31d76f KS |
324 | struct iattr newattrs; |
325 | /* | |
326 | * before unlinking this node, reset permissions | |
327 | * of possible references like hardlinks | |
328 | */ | |
329 | newattrs.ia_uid = 0; | |
330 | newattrs.ia_gid = 0; | |
331 | newattrs.ia_mode = stat.mode & ~0777; | |
332 | newattrs.ia_valid = | |
333 | ATTR_UID|ATTR_GID|ATTR_MODE; | |
334 | mutex_lock(&dentry->d_inode->i_mutex); | |
335 | notify_change(dentry, &newattrs); | |
336 | mutex_unlock(&dentry->d_inode->i_mutex); | |
2b2af54a KS |
337 | err = vfs_unlink(nd.path.dentry->d_inode, |
338 | dentry); | |
339 | if (!err || err == -ENOENT) | |
340 | deleted = 1; | |
341 | } | |
342 | } else { | |
343 | err = -ENOENT; | |
344 | } | |
345 | dput(dentry); | |
346 | } else { | |
347 | err = PTR_ERR(dentry); | |
348 | } | |
349 | mutex_unlock(&nd.path.dentry->d_inode->i_mutex); | |
350 | ||
351 | path_put(&nd.path); | |
352 | if (deleted && strchr(nodename, '/')) | |
353 | delete_path(nodename); | |
2b2af54a KS |
354 | return err; |
355 | } | |
356 | ||
357 | /* | |
358 | * If configured, or requested by the commandline, devtmpfs will be | |
359 | * auto-mounted after the kernel mounted the root filesystem. | |
360 | */ | |
073120cc | 361 | int devtmpfs_mount(const char *mntdir) |
2b2af54a | 362 | { |
2b2af54a KS |
363 | int err; |
364 | ||
fc14f2fe | 365 | if (!mount_dev) |
2b2af54a KS |
366 | return 0; |
367 | ||
2780f1ff | 368 | if (!thread) |
2b2af54a KS |
369 | return 0; |
370 | ||
073120cc | 371 | err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL); |
2b2af54a KS |
372 | if (err) |
373 | printk(KERN_INFO "devtmpfs: error mounting %i\n", err); | |
374 | else | |
375 | printk(KERN_INFO "devtmpfs: mounted\n"); | |
2b2af54a KS |
376 | return err; |
377 | } | |
378 | ||
f9e0b159 | 379 | static DECLARE_COMPLETION(setup_done); |
2780f1ff | 380 | |
fbd48a69 | 381 | static int handle(const char *name, umode_t mode, struct device *dev) |
2780f1ff AV |
382 | { |
383 | if (mode) | |
384 | return handle_create(name, mode, dev); | |
385 | else | |
386 | return handle_remove(name, dev); | |
387 | } | |
388 | ||
389 | static int devtmpfsd(void *p) | |
390 | { | |
391 | char options[] = "mode=0755"; | |
392 | int *err = p; | |
393 | *err = sys_unshare(CLONE_NEWNS); | |
394 | if (*err) | |
395 | goto out; | |
396 | *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options); | |
397 | if (*err) | |
398 | goto out; | |
399 | sys_chdir("/.."); /* will traverse into overmounted root */ | |
400 | sys_chroot("."); | |
401 | complete(&setup_done); | |
402 | while (1) { | |
403 | spin_lock(&req_lock); | |
404 | while (requests) { | |
405 | struct req *req = requests; | |
406 | requests = NULL; | |
407 | spin_unlock(&req_lock); | |
408 | while (req) { | |
e13889ba | 409 | struct req *next = req->next; |
2780f1ff AV |
410 | req->err = handle(req->name, req->mode, req->dev); |
411 | complete(&req->done); | |
e13889ba | 412 | req = next; |
2780f1ff AV |
413 | } |
414 | spin_lock(&req_lock); | |
415 | } | |
65e6757b | 416 | __set_current_state(TASK_INTERRUPTIBLE); |
2780f1ff AV |
417 | spin_unlock(&req_lock); |
418 | schedule(); | |
2780f1ff AV |
419 | } |
420 | return 0; | |
421 | out: | |
422 | complete(&setup_done); | |
423 | return *err; | |
424 | } | |
425 | ||
2b2af54a KS |
426 | /* |
427 | * Create devtmpfs instance, driver-core devices will add their device | |
428 | * nodes here. | |
429 | */ | |
430 | int __init devtmpfs_init(void) | |
431 | { | |
2780f1ff | 432 | int err = register_filesystem(&dev_fs_type); |
2b2af54a KS |
433 | if (err) { |
434 | printk(KERN_ERR "devtmpfs: unable to register devtmpfs " | |
435 | "type %i\n", err); | |
436 | return err; | |
437 | } | |
438 | ||
2780f1ff AV |
439 | thread = kthread_run(devtmpfsd, &err, "kdevtmpfs"); |
440 | if (!IS_ERR(thread)) { | |
441 | wait_for_completion(&setup_done); | |
442 | } else { | |
443 | err = PTR_ERR(thread); | |
444 | thread = NULL; | |
445 | } | |
446 | ||
447 | if (err) { | |
2b2af54a KS |
448 | printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); |
449 | unregister_filesystem(&dev_fs_type); | |
450 | return err; | |
451 | } | |
2b2af54a KS |
452 | |
453 | printk(KERN_INFO "devtmpfs: initialized\n"); | |
454 | return 0; | |
455 | } |