Blame view
drivers/base/devtmpfs.c
8.99 KB
2b2af54a5 Driver Core: devt... |
1 2 3 4 5 6 7 8 |
/* * devtmpfs - kernel-maintained tmpfs-based /dev * * Copyright (C) 2009, Kay Sievers <kay.sievers@vrfy.org> * * During bootup, before any driver core device is registered, * devtmpfs, a tmpfs-based filesystem is created. Every driver-core * device which requests a device node, will add a node in this |
e454cea20 Driver-Core: exte... |
9 |
* filesystem. |
02fbe5e61 devtmpfs: fix 'th... |
10 11 12 |
* By default, all devices are named after the name of the device, * owned by root and have a default mode of 0600. Subsystems can * overwrite the default setting if needed. |
2b2af54a5 Driver Core: devt... |
13 14 15 16 17 18 19 20 21 22 |
*/ #include <linux/kernel.h> #include <linux/syscalls.h> #include <linux/mount.h> #include <linux/device.h> #include <linux/genhd.h> #include <linux/namei.h> #include <linux/fs.h> #include <linux/shmem_fs.h> |
da5e4ef7f devtmpfs: support... |
23 |
#include <linux/ramfs.h> |
e454cea20 Driver-Core: exte... |
24 |
#include <linux/sched.h> |
5a0e3ad6a include cleanup: ... |
25 |
#include <linux/slab.h> |
2780f1ff6 switch devtmpfs o... |
26 |
#include <linux/kthread.h> |
c3a304209 devtmpfs: add bas... |
27 |
#include "base.h" |
2b2af54a5 Driver Core: devt... |
28 |
|
2780f1ff6 switch devtmpfs o... |
29 |
static struct task_struct *thread; |
2b2af54a5 Driver Core: devt... |
30 31 |
#if defined CONFIG_DEVTMPFS_MOUNT |
fc14f2fef convert get_sb_si... |
32 |
static int mount_dev = 1; |
2b2af54a5 Driver Core: devt... |
33 |
#else |
fc14f2fef convert get_sb_si... |
34 |
static int mount_dev; |
2b2af54a5 Driver Core: devt... |
35 |
#endif |
2780f1ff6 switch devtmpfs o... |
36 37 38 39 40 41 42 |
static DEFINE_SPINLOCK(req_lock); static struct req { struct req *next; struct completion done; int err; const char *name; |
2c9ede55e switch device_get... |
43 |
umode_t mode; /* 0 => delete */ |
4e4098a3e driver core: hand... |
44 45 |
kuid_t uid; kgid_t gid; |
2780f1ff6 switch devtmpfs o... |
46 47 |
struct device *dev; } *requests; |
ed413ae6e Driver core: devt... |
48 |
|
2b2af54a5 Driver Core: devt... |
49 50 |
static int __init mount_param(char *str) { |
fc14f2fef convert get_sb_si... |
51 |
mount_dev = simple_strtoul(str, NULL, 0); |
2b2af54a5 Driver Core: devt... |
52 53 54 |
return 1; } __setup("devtmpfs.mount=", mount_param); |
fc14f2fef convert get_sb_si... |
55 56 |
static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) |
2b2af54a5 Driver Core: devt... |
57 |
{ |
da5e4ef7f devtmpfs: support... |
58 |
#ifdef CONFIG_TMPFS |
fc14f2fef convert get_sb_si... |
59 |
return mount_single(fs_type, flags, data, shmem_fill_super); |
da5e4ef7f devtmpfs: support... |
60 |
#else |
fc14f2fef convert get_sb_si... |
61 |
return mount_single(fs_type, flags, data, ramfs_fill_super); |
da5e4ef7f devtmpfs: support... |
62 |
#endif |
2b2af54a5 Driver Core: devt... |
63 64 65 66 |
} static struct file_system_type dev_fs_type = { .name = "devtmpfs", |
fc14f2fef convert get_sb_si... |
67 |
.mount = dev_mount, |
2b2af54a5 Driver Core: devt... |
68 69 70 71 72 73 74 75 76 77 78 |
.kill_sb = kill_litter_super, }; #ifdef CONFIG_BLOCK static inline int is_blockdev(struct device *dev) { return dev->class == &block_class; } #else static inline int is_blockdev(struct device *dev) { return 0; } #endif |
2780f1ff6 switch devtmpfs o... |
79 80 81 82 83 84 85 86 87 |
int devtmpfs_create_node(struct device *dev) { const char *tmp = NULL; struct req req; if (!thread) return 0; req.mode = 0; |
4e4098a3e driver core: hand... |
88 89 |
req.uid = GLOBAL_ROOT_UID; req.gid = GLOBAL_ROOT_GID; |
3c2670e65 driver core: add ... |
90 |
req.name = device_get_devnode(dev, &req.mode, &req.uid, &req.gid, &tmp); |
2780f1ff6 switch devtmpfs o... |
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
if (!req.name) return -ENOMEM; if (req.mode == 0) req.mode = 0600; if (is_blockdev(dev)) req.mode |= S_IFBLK; else req.mode |= S_IFCHR; req.dev = dev; init_completion(&req.done); spin_lock(&req_lock); req.next = requests; requests = &req; spin_unlock(&req_lock); wake_up_process(thread); wait_for_completion(&req.done); kfree(tmp); return req.err; } int devtmpfs_delete_node(struct device *dev) { const char *tmp = NULL; struct req req; if (!thread) return 0; |
3c2670e65 driver core: add ... |
125 |
req.name = device_get_devnode(dev, NULL, NULL, NULL, &tmp); |
2780f1ff6 switch devtmpfs o... |
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
if (!req.name) return -ENOMEM; req.mode = 0; req.dev = dev; init_completion(&req.done); spin_lock(&req_lock); req.next = requests; requests = &req; spin_unlock(&req_lock); wake_up_process(thread); wait_for_completion(&req.done); kfree(tmp); return req.err; } |
fbd48a69a switch devtmpfs t... |
145 |
static int dev_mkdir(const char *name, umode_t mode) |
2b2af54a5 Driver Core: devt... |
146 |
{ |
2b2af54a5 Driver Core: devt... |
147 |
struct dentry *dentry; |
69753a0f1 switch devtmpfs t... |
148 |
struct path path; |
2b2af54a5 Driver Core: devt... |
149 |
int err; |
1ac12b4b6 vfs: turn is_dir ... |
150 |
dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); |
69753a0f1 switch devtmpfs t... |
151 152 |
if (IS_ERR(dentry)) return PTR_ERR(dentry); |
75c3cfa85 VFS: assorted wei... |
153 |
err = vfs_mkdir(d_inode(path.dentry), dentry, mode); |
69753a0f1 switch devtmpfs t... |
154 155 |
if (!err) /* mark as kernel-created inode */ |
75c3cfa85 VFS: assorted wei... |
156 |
d_inode(dentry)->i_private = &thread; |
921a1650d new helper: done_... |
157 |
done_path_create(&path, dentry); |
2b2af54a5 Driver Core: devt... |
158 159 160 161 162 |
return err; } static int create_path(const char *nodepath) { |
5da4e6894 devtmpfs: get rid... |
163 164 |
char *path; char *s; |
9d108d254 devtmpfs: missing... |
165 |
int err = 0; |
2b2af54a5 Driver Core: devt... |
166 |
|
5da4e6894 devtmpfs: get rid... |
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
/* parent directories do not exist, create them */ path = kstrdup(nodepath, GFP_KERNEL); if (!path) return -ENOMEM; s = path; for (;;) { s = strchr(s, '/'); if (!s) break; s[0] = '\0'; err = dev_mkdir(path, 0755); if (err && err != -EEXIST) break; s[0] = '/'; s++; |
2b2af54a5 Driver Core: devt... |
183 |
} |
5da4e6894 devtmpfs: get rid... |
184 |
kfree(path); |
2b2af54a5 Driver Core: devt... |
185 186 |
return err; } |
4e4098a3e driver core: hand... |
187 188 |
static int handle_create(const char *nodename, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) |
2b2af54a5 Driver Core: devt... |
189 |
{ |
2b2af54a5 Driver Core: devt... |
190 |
struct dentry *dentry; |
69753a0f1 switch devtmpfs t... |
191 |
struct path path; |
2b2af54a5 Driver Core: devt... |
192 |
int err; |
69753a0f1 switch devtmpfs t... |
193 194 |
dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); if (dentry == ERR_PTR(-ENOENT)) { |
2b2af54a5 Driver Core: devt... |
195 |
create_path(nodename); |
69753a0f1 switch devtmpfs t... |
196 |
dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); |
2b2af54a5 Driver Core: devt... |
197 |
} |
69753a0f1 switch devtmpfs t... |
198 199 |
if (IS_ERR(dentry)) return PTR_ERR(dentry); |
75c3cfa85 VFS: assorted wei... |
200 |
err = vfs_mknod(d_inode(path.dentry), dentry, mode, dev->devt); |
69753a0f1 switch devtmpfs t... |
201 202 |
if (!err) { struct iattr newattrs; |
69753a0f1 switch devtmpfs t... |
203 |
newattrs.ia_mode = mode; |
4e4098a3e driver core: hand... |
204 205 |
newattrs.ia_uid = uid; newattrs.ia_gid = gid; |
3c2670e65 driver core: add ... |
206 |
newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; |
5955102c9 wrappers for ->i_... |
207 |
inode_lock(d_inode(dentry)); |
27ac0ffea locks: break dele... |
208 |
notify_change(dentry, &newattrs, NULL); |
5955102c9 wrappers for ->i_... |
209 |
inode_unlock(d_inode(dentry)); |
69753a0f1 switch devtmpfs t... |
210 211 |
/* mark as kernel-created inode */ |
75c3cfa85 VFS: assorted wei... |
212 |
d_inode(dentry)->i_private = &thread; |
2b2af54a5 Driver Core: devt... |
213 |
} |
921a1650d new helper: done_... |
214 |
done_path_create(&path, dentry); |
2b2af54a5 Driver Core: devt... |
215 216 217 218 219 |
return err; } static int dev_rmdir(const char *name) { |
79714f72d get rid of kern_p... |
220 |
struct path parent; |
2b2af54a5 Driver Core: devt... |
221 222 |
struct dentry *dentry; int err; |
79714f72d get rid of kern_p... |
223 224 225 |
dentry = kern_path_locked(name, &parent); if (IS_ERR(dentry)) return PTR_ERR(dentry); |
75c3cfa85 VFS: assorted wei... |
226 227 228 |
if (d_really_is_positive(dentry)) { if (d_inode(dentry)->i_private == &thread) err = vfs_rmdir(d_inode(parent.dentry), dentry); |
79714f72d get rid of kern_p... |
229 230 |
else err = -EPERM; |
2b2af54a5 Driver Core: devt... |
231 |
} else { |
79714f72d get rid of kern_p... |
232 |
err = -ENOENT; |
2b2af54a5 Driver Core: devt... |
233 |
} |
79714f72d get rid of kern_p... |
234 |
dput(dentry); |
5955102c9 wrappers for ->i_... |
235 |
inode_unlock(d_inode(parent.dentry)); |
79714f72d get rid of kern_p... |
236 |
path_put(&parent); |
2b2af54a5 Driver Core: devt... |
237 238 239 240 241 242 243 244 245 246 247 |
return err; } static int delete_path(const char *nodepath) { const char *path; int err = 0; path = kstrdup(nodepath, GFP_KERNEL); if (!path) return -ENOMEM; |
ed413ae6e Driver core: devt... |
248 |
for (;;) { |
2b2af54a5 Driver Core: devt... |
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
char *base; base = strrchr(path, '/'); if (!base) break; base[0] = '\0'; err = dev_rmdir(path); if (err) break; } kfree(path); return err; } static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat) { /* did we create it */ |
2780f1ff6 switch devtmpfs o... |
267 |
if (inode->i_private != &thread) |
2b2af54a5 Driver Core: devt... |
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
return 0; /* does the dev_t match */ if (is_blockdev(dev)) { if (!S_ISBLK(stat->mode)) return 0; } else { if (!S_ISCHR(stat->mode)) return 0; } if (stat->rdev != dev->devt) return 0; /* ours */ return 1; } |
2780f1ff6 switch devtmpfs o... |
284 |
static int handle_remove(const char *nodename, struct device *dev) |
2b2af54a5 Driver Core: devt... |
285 |
{ |
79714f72d get rid of kern_p... |
286 |
struct path parent; |
2b2af54a5 Driver Core: devt... |
287 |
struct dentry *dentry; |
fbde7c611 devtmpfs: Calling... |
288 |
int deleted = 0; |
2b2af54a5 Driver Core: devt... |
289 |
int err; |
79714f72d get rid of kern_p... |
290 291 292 |
dentry = kern_path_locked(nodename, &parent); if (IS_ERR(dentry)) return PTR_ERR(dentry); |
75c3cfa85 VFS: assorted wei... |
293 |
if (d_really_is_positive(dentry)) { |
79714f72d get rid of kern_p... |
294 |
struct kstat stat; |
3dadecce2 switch vfs_getatt... |
295 296 |
struct path p = {.mnt = parent.mnt, .dentry = dentry}; err = vfs_getattr(&p, &stat); |
75c3cfa85 VFS: assorted wei... |
297 |
if (!err && dev_mynode(dev, d_inode(dentry), &stat)) { |
79714f72d get rid of kern_p... |
298 299 300 301 302 |
struct iattr newattrs; /* * before unlinking this node, reset permissions * of possible references like hardlinks */ |
91fa2ccaa userns: Convert d... |
303 304 |
newattrs.ia_uid = GLOBAL_ROOT_UID; newattrs.ia_gid = GLOBAL_ROOT_GID; |
79714f72d get rid of kern_p... |
305 306 307 |
newattrs.ia_mode = stat.mode & ~0777; newattrs.ia_valid = ATTR_UID|ATTR_GID|ATTR_MODE; |
5955102c9 wrappers for ->i_... |
308 |
inode_lock(d_inode(dentry)); |
27ac0ffea locks: break dele... |
309 |
notify_change(dentry, &newattrs, NULL); |
5955102c9 wrappers for ->i_... |
310 |
inode_unlock(d_inode(dentry)); |
75c3cfa85 VFS: assorted wei... |
311 |
err = vfs_unlink(d_inode(parent.dentry), dentry, NULL); |
79714f72d get rid of kern_p... |
312 313 |
if (!err || err == -ENOENT) deleted = 1; |
2b2af54a5 Driver Core: devt... |
314 |
} |
2b2af54a5 Driver Core: devt... |
315 |
} else { |
79714f72d get rid of kern_p... |
316 |
err = -ENOENT; |
2b2af54a5 Driver Core: devt... |
317 |
} |
79714f72d get rid of kern_p... |
318 |
dput(dentry); |
5955102c9 wrappers for ->i_... |
319 |
inode_unlock(d_inode(parent.dentry)); |
2b2af54a5 Driver Core: devt... |
320 |
|
79714f72d get rid of kern_p... |
321 |
path_put(&parent); |
2b2af54a5 Driver Core: devt... |
322 323 |
if (deleted && strchr(nodename, '/')) delete_path(nodename); |
2b2af54a5 Driver Core: devt... |
324 325 326 327 328 329 330 |
return err; } /* * If configured, or requested by the commandline, devtmpfs will be * auto-mounted after the kernel mounted the root filesystem. */ |
073120cc2 Driver Core: devt... |
331 |
int devtmpfs_mount(const char *mntdir) |
2b2af54a5 Driver Core: devt... |
332 |
{ |
2b2af54a5 Driver Core: devt... |
333 |
int err; |
fc14f2fef convert get_sb_si... |
334 |
if (!mount_dev) |
2b2af54a5 Driver Core: devt... |
335 |
return 0; |
2780f1ff6 switch devtmpfs o... |
336 |
if (!thread) |
2b2af54a5 Driver Core: devt... |
337 |
return 0; |
073120cc2 Driver Core: devt... |
338 |
err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL); |
2b2af54a5 Driver Core: devt... |
339 340 341 342 343 344 |
if (err) printk(KERN_INFO "devtmpfs: error mounting %i ", err); else printk(KERN_INFO "devtmpfs: mounted "); |
2b2af54a5 Driver Core: devt... |
345 346 |
return err; } |
f9e0b159d drivers/base/devt... |
347 |
static DECLARE_COMPLETION(setup_done); |
2780f1ff6 switch devtmpfs o... |
348 |
|
4e4098a3e driver core: hand... |
349 |
static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, |
3c2670e65 driver core: add ... |
350 |
struct device *dev) |
2780f1ff6 switch devtmpfs o... |
351 352 |
{ if (mode) |
3c2670e65 driver core: add ... |
353 |
return handle_create(name, mode, uid, gid, dev); |
2780f1ff6 switch devtmpfs o... |
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
else return handle_remove(name, dev); } static int devtmpfsd(void *p) { char options[] = "mode=0755"; int *err = p; *err = sys_unshare(CLONE_NEWNS); if (*err) goto out; *err = sys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options); if (*err) goto out; sys_chdir("/.."); /* will traverse into overmounted root */ sys_chroot("."); complete(&setup_done); while (1) { spin_lock(&req_lock); while (requests) { struct req *req = requests; requests = NULL; spin_unlock(&req_lock); while (req) { |
e13889bab fix devtmpfs race |
378 |
struct req *next = req->next; |
3c2670e65 driver core: add ... |
379 380 |
req->err = handle(req->name, req->mode, req->uid, req->gid, req->dev); |
2780f1ff6 switch devtmpfs o... |
381 |
complete(&req->done); |
e13889bab fix devtmpfs race |
382 |
req = next; |
2780f1ff6 switch devtmpfs o... |
383 384 385 |
} spin_lock(&req_lock); } |
65e6757be devtmpfsd: fix ta... |
386 |
__set_current_state(TASK_INTERRUPTIBLE); |
2780f1ff6 switch devtmpfs o... |
387 388 |
spin_unlock(&req_lock); schedule(); |
2780f1ff6 switch devtmpfs o... |
389 390 391 392 393 394 |
} return 0; out: complete(&setup_done); return *err; } |
2b2af54a5 Driver Core: devt... |
395 396 397 398 399 400 |
/* * Create devtmpfs instance, driver-core devices will add their device * nodes here. */ int __init devtmpfs_init(void) { |
2780f1ff6 switch devtmpfs o... |
401 |
int err = register_filesystem(&dev_fs_type); |
2b2af54a5 Driver Core: devt... |
402 403 404 405 406 407 |
if (err) { printk(KERN_ERR "devtmpfs: unable to register devtmpfs " "type %i ", err); return err; } |
2780f1ff6 switch devtmpfs o... |
408 409 410 411 412 413 414 415 416 |
thread = kthread_run(devtmpfsd, &err, "kdevtmpfs"); if (!IS_ERR(thread)) { wait_for_completion(&setup_done); } else { err = PTR_ERR(thread); thread = NULL; } if (err) { |
2b2af54a5 Driver Core: devt... |
417 418 419 420 421 |
printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i ", err); unregister_filesystem(&dev_fs_type); return err; } |
2b2af54a5 Driver Core: devt... |
422 423 424 425 426 |
printk(KERN_INFO "devtmpfs: initialized "); return 0; } |