]>
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 | 9 | * filesystem. |
02fbe5e6 PK |
10 | * By default, all devices are named after the name of the device, |
11 | * owned by root and have a default mode of 0600. Subsystems can | |
12 | * 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> |
c3a30420 | 27 | #include "base.h" |
2b2af54a | 28 | |
2780f1ff | 29 | static struct task_struct *thread; |
2b2af54a KS |
30 | |
31 | #if defined CONFIG_DEVTMPFS_MOUNT | |
fc14f2fe | 32 | static int mount_dev = 1; |
2b2af54a | 33 | #else |
fc14f2fe | 34 | static int mount_dev; |
2b2af54a KS |
35 | #endif |
36 | ||
2780f1ff AV |
37 | static DEFINE_SPINLOCK(req_lock); |
38 | ||
39 | static struct req { | |
40 | struct req *next; | |
41 | struct completion done; | |
42 | int err; | |
43 | const char *name; | |
2c9ede55 | 44 | umode_t mode; /* 0 => delete */ |
4e4098a3 GKH |
45 | kuid_t uid; |
46 | kgid_t gid; | |
2780f1ff AV |
47 | struct device *dev; |
48 | } *requests; | |
ed413ae6 | 49 | |
2b2af54a KS |
50 | static int __init mount_param(char *str) |
51 | { | |
fc14f2fe | 52 | mount_dev = simple_strtoul(str, NULL, 0); |
2b2af54a KS |
53 | return 1; |
54 | } | |
55 | __setup("devtmpfs.mount=", mount_param); | |
56 | ||
fc14f2fe AV |
57 | static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, |
58 | const char *dev_name, void *data) | |
2b2af54a | 59 | { |
da5e4ef7 | 60 | #ifdef CONFIG_TMPFS |
fc14f2fe | 61 | return mount_single(fs_type, flags, data, shmem_fill_super); |
da5e4ef7 | 62 | #else |
fc14f2fe | 63 | return mount_single(fs_type, flags, data, ramfs_fill_super); |
da5e4ef7 | 64 | #endif |
2b2af54a KS |
65 | } |
66 | ||
67 | static struct file_system_type dev_fs_type = { | |
68 | .name = "devtmpfs", | |
fc14f2fe | 69 | .mount = dev_mount, |
2b2af54a KS |
70 | .kill_sb = kill_litter_super, |
71 | }; | |
72 | ||
73 | #ifdef CONFIG_BLOCK | |
74 | static inline int is_blockdev(struct device *dev) | |
75 | { | |
76 | return dev->class == &block_class; | |
77 | } | |
78 | #else | |
79 | static inline int is_blockdev(struct device *dev) { return 0; } | |
80 | #endif | |
81 | ||
2780f1ff AV |
82 | int devtmpfs_create_node(struct device *dev) |
83 | { | |
84 | const char *tmp = NULL; | |
85 | struct req req; | |
86 | ||
87 | if (!thread) | |
88 | return 0; | |
89 | ||
90 | req.mode = 0; | |
4e4098a3 GKH |
91 | req.uid = GLOBAL_ROOT_UID; |
92 | req.gid = GLOBAL_ROOT_GID; | |
3c2670e6 | 93 | req.name = device_get_devnode(dev, &req.mode, &req.uid, &req.gid, &tmp); |
2780f1ff AV |
94 | if (!req.name) |
95 | return -ENOMEM; | |
96 | ||
97 | if (req.mode == 0) | |
98 | req.mode = 0600; | |
99 | if (is_blockdev(dev)) | |
100 | req.mode |= S_IFBLK; | |
101 | else | |
102 | req.mode |= S_IFCHR; | |
103 | ||
104 | req.dev = dev; | |
105 | ||
106 | init_completion(&req.done); | |
107 | ||
108 | spin_lock(&req_lock); | |
109 | req.next = requests; | |
110 | requests = &req; | |
111 | spin_unlock(&req_lock); | |
112 | ||
113 | wake_up_process(thread); | |
114 | wait_for_completion(&req.done); | |
115 | ||
116 | kfree(tmp); | |
117 | ||
118 | return req.err; | |
119 | } | |
120 | ||
121 | int devtmpfs_delete_node(struct device *dev) | |
122 | { | |
123 | const char *tmp = NULL; | |
124 | struct req req; | |
125 | ||
126 | if (!thread) | |
127 | return 0; | |
128 | ||
3c2670e6 | 129 | req.name = device_get_devnode(dev, NULL, NULL, NULL, &tmp); |
2780f1ff AV |
130 | if (!req.name) |
131 | return -ENOMEM; | |
132 | ||
133 | req.mode = 0; | |
134 | req.dev = dev; | |
135 | ||
136 | init_completion(&req.done); | |
137 | ||
138 | spin_lock(&req_lock); | |
139 | req.next = requests; | |
140 | requests = &req; | |
141 | spin_unlock(&req_lock); | |
142 | ||
143 | wake_up_process(thread); | |
144 | wait_for_completion(&req.done); | |
145 | ||
146 | kfree(tmp); | |
147 | return req.err; | |
148 | } | |
149 | ||
fbd48a69 | 150 | static int dev_mkdir(const char *name, umode_t mode) |
2b2af54a | 151 | { |
2b2af54a | 152 | struct dentry *dentry; |
69753a0f | 153 | struct path path; |
2b2af54a KS |
154 | int err; |
155 | ||
1ac12b4b | 156 | dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); |
69753a0f AV |
157 | if (IS_ERR(dentry)) |
158 | return PTR_ERR(dentry); | |
159 | ||
160 | err = vfs_mkdir(path.dentry->d_inode, dentry, mode); | |
161 | if (!err) | |
162 | /* mark as kernel-created inode */ | |
163 | dentry->d_inode->i_private = &thread; | |
921a1650 | 164 | done_path_create(&path, dentry); |
2b2af54a KS |
165 | return err; |
166 | } | |
167 | ||
168 | static int create_path(const char *nodepath) | |
169 | { | |
5da4e689 AV |
170 | char *path; |
171 | char *s; | |
9d108d25 | 172 | int err = 0; |
2b2af54a | 173 | |
5da4e689 AV |
174 | /* parent directories do not exist, create them */ |
175 | path = kstrdup(nodepath, GFP_KERNEL); | |
176 | if (!path) | |
177 | return -ENOMEM; | |
178 | ||
179 | s = path; | |
180 | for (;;) { | |
181 | s = strchr(s, '/'); | |
182 | if (!s) | |
183 | break; | |
184 | s[0] = '\0'; | |
185 | err = dev_mkdir(path, 0755); | |
186 | if (err && err != -EEXIST) | |
187 | break; | |
188 | s[0] = '/'; | |
189 | s++; | |
2b2af54a | 190 | } |
5da4e689 | 191 | kfree(path); |
2b2af54a KS |
192 | return err; |
193 | } | |
194 | ||
4e4098a3 GKH |
195 | static int handle_create(const char *nodename, umode_t mode, kuid_t uid, |
196 | kgid_t gid, struct device *dev) | |
2b2af54a | 197 | { |
2b2af54a | 198 | struct dentry *dentry; |
69753a0f | 199 | struct path path; |
2b2af54a KS |
200 | int err; |
201 | ||
69753a0f AV |
202 | dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); |
203 | if (dentry == ERR_PTR(-ENOENT)) { | |
2b2af54a | 204 | create_path(nodename); |
69753a0f | 205 | dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); |
2b2af54a | 206 | } |
69753a0f AV |
207 | if (IS_ERR(dentry)) |
208 | return PTR_ERR(dentry); | |
209 | ||
3c2670e6 | 210 | err = vfs_mknod(path.dentry->d_inode, dentry, mode, dev->devt); |
69753a0f AV |
211 | if (!err) { |
212 | struct iattr newattrs; | |
213 | ||
69753a0f | 214 | newattrs.ia_mode = mode; |
4e4098a3 GKH |
215 | newattrs.ia_uid = uid; |
216 | newattrs.ia_gid = gid; | |
3c2670e6 | 217 | newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; |
69753a0f | 218 | mutex_lock(&dentry->d_inode->i_mutex); |
27ac0ffe | 219 | notify_change(dentry, &newattrs, NULL); |
69753a0f AV |
220 | mutex_unlock(&dentry->d_inode->i_mutex); |
221 | ||
222 | /* mark as kernel-created inode */ | |
223 | dentry->d_inode->i_private = &thread; | |
2b2af54a | 224 | } |
921a1650 | 225 | done_path_create(&path, dentry); |
2b2af54a KS |
226 | return err; |
227 | } | |
228 | ||
229 | static int dev_rmdir(const char *name) | |
230 | { | |
79714f72 | 231 | struct path parent; |
2b2af54a KS |
232 | struct dentry *dentry; |
233 | int err; | |
234 | ||
79714f72 AV |
235 | dentry = kern_path_locked(name, &parent); |
236 | if (IS_ERR(dentry)) | |
237 | return PTR_ERR(dentry); | |
238 | if (dentry->d_inode) { | |
239 | if (dentry->d_inode->i_private == &thread) | |
240 | err = vfs_rmdir(parent.dentry->d_inode, dentry); | |
241 | else | |
242 | err = -EPERM; | |
2b2af54a | 243 | } else { |
79714f72 | 244 | err = -ENOENT; |
2b2af54a | 245 | } |
79714f72 AV |
246 | dput(dentry); |
247 | mutex_unlock(&parent.dentry->d_inode->i_mutex); | |
248 | path_put(&parent); | |
2b2af54a KS |
249 | return err; |
250 | } | |
251 | ||
252 | static int delete_path(const char *nodepath) | |
253 | { | |
254 | const char *path; | |
255 | int err = 0; | |
256 | ||
257 | path = kstrdup(nodepath, GFP_KERNEL); | |
258 | if (!path) | |
259 | return -ENOMEM; | |
260 | ||
ed413ae6 | 261 | for (;;) { |
2b2af54a KS |
262 | char *base; |
263 | ||
264 | base = strrchr(path, '/'); | |
265 | if (!base) | |
266 | break; | |
267 | base[0] = '\0'; | |
268 | err = dev_rmdir(path); | |
269 | if (err) | |
270 | break; | |
271 | } | |
272 | ||
273 | kfree(path); | |
274 | return err; | |
275 | } | |
276 | ||
277 | static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat) | |
278 | { | |
279 | /* did we create it */ | |
2780f1ff | 280 | if (inode->i_private != &thread) |
2b2af54a KS |
281 | return 0; |
282 | ||
283 | /* does the dev_t match */ | |
284 | if (is_blockdev(dev)) { | |
285 | if (!S_ISBLK(stat->mode)) | |
286 | return 0; | |
287 | } else { | |
288 | if (!S_ISCHR(stat->mode)) | |
289 | return 0; | |
290 | } | |
291 | if (stat->rdev != dev->devt) | |
292 | return 0; | |
293 | ||
294 | /* ours */ | |
295 | return 1; | |
296 | } | |
297 | ||
2780f1ff | 298 | static int handle_remove(const char *nodename, struct device *dev) |
2b2af54a | 299 | { |
79714f72 | 300 | struct path parent; |
2b2af54a | 301 | struct dentry *dentry; |
2b2af54a KS |
302 | int deleted = 1; |
303 | int err; | |
304 | ||
79714f72 AV |
305 | dentry = kern_path_locked(nodename, &parent); |
306 | if (IS_ERR(dentry)) | |
307 | return PTR_ERR(dentry); | |
308 | ||
309 | if (dentry->d_inode) { | |
310 | struct kstat stat; | |
3dadecce AV |
311 | struct path p = {.mnt = parent.mnt, .dentry = dentry}; |
312 | err = vfs_getattr(&p, &stat); | |
79714f72 AV |
313 | if (!err && dev_mynode(dev, dentry->d_inode, &stat)) { |
314 | struct iattr newattrs; | |
315 | /* | |
316 | * before unlinking this node, reset permissions | |
317 | * of possible references like hardlinks | |
318 | */ | |
91fa2cca EB |
319 | newattrs.ia_uid = GLOBAL_ROOT_UID; |
320 | newattrs.ia_gid = GLOBAL_ROOT_GID; | |
79714f72 AV |
321 | newattrs.ia_mode = stat.mode & ~0777; |
322 | newattrs.ia_valid = | |
323 | ATTR_UID|ATTR_GID|ATTR_MODE; | |
324 | mutex_lock(&dentry->d_inode->i_mutex); | |
27ac0ffe | 325 | notify_change(dentry, &newattrs, NULL); |
79714f72 | 326 | mutex_unlock(&dentry->d_inode->i_mutex); |
b21996e3 | 327 | err = vfs_unlink(parent.dentry->d_inode, dentry, NULL); |
79714f72 AV |
328 | if (!err || err == -ENOENT) |
329 | deleted = 1; | |
2b2af54a | 330 | } |
2b2af54a | 331 | } else { |
79714f72 | 332 | err = -ENOENT; |
2b2af54a | 333 | } |
79714f72 AV |
334 | dput(dentry); |
335 | mutex_unlock(&parent.dentry->d_inode->i_mutex); | |
2b2af54a | 336 | |
79714f72 | 337 | path_put(&parent); |
2b2af54a KS |
338 | if (deleted && strchr(nodename, '/')) |
339 | delete_path(nodename); | |
2b2af54a KS |
340 | return err; |
341 | } | |
342 | ||
343 | /* | |
344 | * If configured, or requested by the commandline, devtmpfs will be | |
345 | * auto-mounted after the kernel mounted the root filesystem. | |
346 | */ | |
073120cc | 347 | int devtmpfs_mount(const char *mntdir) |
2b2af54a | 348 | { |
2b2af54a KS |
349 | int err; |
350 | ||
fc14f2fe | 351 | if (!mount_dev) |
2b2af54a KS |
352 | return 0; |
353 | ||
2780f1ff | 354 | if (!thread) |
2b2af54a KS |
355 | return 0; |
356 | ||
073120cc | 357 | err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL); |
2b2af54a KS |
358 | if (err) |
359 | printk(KERN_INFO "devtmpfs: error mounting %i\n", err); | |
360 | else | |
361 | printk(KERN_INFO "devtmpfs: mounted\n"); | |
2b2af54a KS |
362 | return err; |
363 | } | |
364 | ||
f9e0b159 | 365 | static DECLARE_COMPLETION(setup_done); |
2780f1ff | 366 | |
4e4098a3 | 367 | static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, |
3c2670e6 | 368 | struct device *dev) |
2780f1ff AV |
369 | { |
370 | if (mode) | |
3c2670e6 | 371 | return handle_create(name, mode, uid, gid, dev); |
2780f1ff AV |
372 | else |
373 | return handle_remove(name, dev); | |
374 | } | |
375 | ||
376 | static int devtmpfsd(void *p) | |
377 | { | |
378 | char options[] = "mode=0755"; | |
379 | int *err = p; | |
380 | *err = sys_unshare(CLONE_NEWNS); | |
381 | if (*err) | |
382 | goto out; | |
383 | *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options); | |
384 | if (*err) | |
385 | goto out; | |
386 | sys_chdir("/.."); /* will traverse into overmounted root */ | |
387 | sys_chroot("."); | |
388 | complete(&setup_done); | |
389 | while (1) { | |
390 | spin_lock(&req_lock); | |
391 | while (requests) { | |
392 | struct req *req = requests; | |
393 | requests = NULL; | |
394 | spin_unlock(&req_lock); | |
395 | while (req) { | |
e13889ba | 396 | struct req *next = req->next; |
3c2670e6 KS |
397 | req->err = handle(req->name, req->mode, |
398 | req->uid, req->gid, req->dev); | |
2780f1ff | 399 | complete(&req->done); |
e13889ba | 400 | req = next; |
2780f1ff AV |
401 | } |
402 | spin_lock(&req_lock); | |
403 | } | |
65e6757b | 404 | __set_current_state(TASK_INTERRUPTIBLE); |
2780f1ff AV |
405 | spin_unlock(&req_lock); |
406 | schedule(); | |
2780f1ff AV |
407 | } |
408 | return 0; | |
409 | out: | |
410 | complete(&setup_done); | |
411 | return *err; | |
412 | } | |
413 | ||
2b2af54a KS |
414 | /* |
415 | * Create devtmpfs instance, driver-core devices will add their device | |
416 | * nodes here. | |
417 | */ | |
418 | int __init devtmpfs_init(void) | |
419 | { | |
2780f1ff | 420 | int err = register_filesystem(&dev_fs_type); |
2b2af54a KS |
421 | if (err) { |
422 | printk(KERN_ERR "devtmpfs: unable to register devtmpfs " | |
423 | "type %i\n", err); | |
424 | return err; | |
425 | } | |
426 | ||
2780f1ff AV |
427 | thread = kthread_run(devtmpfsd, &err, "kdevtmpfs"); |
428 | if (!IS_ERR(thread)) { | |
429 | wait_for_completion(&setup_done); | |
430 | } else { | |
431 | err = PTR_ERR(thread); | |
432 | thread = NULL; | |
433 | } | |
434 | ||
435 | if (err) { | |
2b2af54a KS |
436 | printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); |
437 | unregister_filesystem(&dev_fs_type); | |
438 | return err; | |
439 | } | |
2b2af54a KS |
440 | |
441 | printk(KERN_INFO "devtmpfs: initialized\n"); | |
442 | return 0; | |
443 | } |