Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
1
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
437589a
Documentation
arch
block
crypto
drivers
accessibility
acpi
amba
ata
atm
auxdisplay
base
power
regmap
Kconfig
Makefile
attribute_container.c
base.h
bus.c
class.c
core.c
cpu.c
dd.c
devres.c
devtmpfs.c
dma-buf.c
dma-coherent.c
dma-contiguous.c
dma-mapping.c
driver.c
firmware.c
firmware_class.c
hypervisor.c
init.c
isa.c
map.c
memory.c
module.c
node.c
platform.c
soc.c
syscore.c
topology.c
transport_class.c
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
cpufreq
cpuidle
crypto
dca
devfreq
dio
dma
edac
eisa
extcon
firewire
firmware
gpio
gpu
hid
hsi
hv
hwmon
hwspinlock
i2c
ide
idle
ieee802154
iio
infiniband
input
iommu
irqchip
isdn
leds
lguest
macintosh
md
media
memory
memstick
message
mfd
misc
mmc
mtd
net
nfc
nubus
of
oprofile
parisc
parport
pci
pcmcia
pinctrl
platform
pnp
power
pps
ps3
ptp
pwm
rapidio
regulator
remoteproc
rpmsg
rtc
s390
sbus
scsi
sfi
sh
sn
spi
ssb
staging
target
tc
thermal
tty
uio
usb
uwb
vfio
vhost
video
virt
virtio
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
drivers
/
base
/
devtmpfs.c
Copy path
Blame
Blame
Latest commit
History
History
434 lines (371 loc) · 8.7 KB
Breadcrumbs
linux
/
drivers
/
base
/
devtmpfs.c
Top
File metadata and controls
Code
Blame
434 lines (371 loc) · 8.7 KB
Raw
/* * 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 * filesystem. * 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. */ #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> #include <linux/ramfs.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/kthread.h> static struct task_struct *thread; #if defined CONFIG_DEVTMPFS_MOUNT static int mount_dev = 1; #else static int mount_dev; #endif static DEFINE_SPINLOCK(req_lock); static struct req { struct req *next; struct completion done; int err; const char *name; umode_t mode; /* 0 => delete */ struct device *dev; } *requests; static int __init mount_param(char *str) { mount_dev = simple_strtoul(str, NULL, 0); return 1; } __setup("devtmpfs.mount=", mount_param); static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { #ifdef CONFIG_TMPFS return mount_single(fs_type, flags, data, shmem_fill_super); #else return mount_single(fs_type, flags, data, ramfs_fill_super); #endif } static struct file_system_type dev_fs_type = { .name = "devtmpfs", .mount = dev_mount, .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 int devtmpfs_create_node(struct device *dev) { const char *tmp = NULL; struct req req; if (!thread) return 0; req.mode = 0; req.name = device_get_devnode(dev, &req.mode, &tmp); 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; req.name = device_get_devnode(dev, NULL, &tmp); 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; } static int dev_mkdir(const char *name, umode_t mode) { struct dentry *dentry; struct path path; int err; dentry = kern_path_create(AT_FDCWD, name, &path, 1); if (IS_ERR(dentry)) return PTR_ERR(dentry); err = vfs_mkdir(path.dentry->d_inode, dentry, mode); if (!err) /* mark as kernel-created inode */ dentry->d_inode->i_private = &thread; done_path_create(&path, dentry); return err; } static int create_path(const char *nodepath) { char *path; char *s; int err = 0; /* 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++; } kfree(path); return err; } static int handle_create(const char *nodename, umode_t mode, struct device *dev) { struct dentry *dentry; struct path path; int err; dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); if (dentry == ERR_PTR(-ENOENT)) { create_path(nodename); dentry = kern_path_create(AT_FDCWD, nodename, &path, 0); } if (IS_ERR(dentry)) return PTR_ERR(dentry); err = vfs_mknod(path.dentry->d_inode, dentry, mode, dev->devt); if (!err) { struct iattr newattrs; /* fixup possibly umasked mode */ newattrs.ia_mode = mode; newattrs.ia_valid = ATTR_MODE; mutex_lock(&dentry->d_inode->i_mutex); notify_change(dentry, &newattrs); mutex_unlock(&dentry->d_inode->i_mutex); /* mark as kernel-created inode */ dentry->d_inode->i_private = &thread; } done_path_create(&path, dentry); return err; } static int dev_rmdir(const char *name) { struct path parent; struct dentry *dentry; int err; dentry = kern_path_locked(name, &parent); if (IS_ERR(dentry)) return PTR_ERR(dentry); if (dentry->d_inode) { if (dentry->d_inode->i_private == &thread) err = vfs_rmdir(parent.dentry->d_inode, dentry); else err = -EPERM; } else { err = -ENOENT; } dput(dentry); mutex_unlock(&parent.dentry->d_inode->i_mutex); path_put(&parent); return err; } static int delete_path(const char *nodepath) { const char *path; int err = 0; path = kstrdup(nodepath, GFP_KERNEL); if (!path) return -ENOMEM; for (;;) { 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 */ if (inode->i_private != &thread) 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; } static int handle_remove(const char *nodename, struct device *dev) { struct path parent; struct dentry *dentry; int deleted = 1; int err; dentry = kern_path_locked(nodename, &parent); if (IS_ERR(dentry)) return PTR_ERR(dentry); if (dentry->d_inode) { struct kstat stat; err = vfs_getattr(parent.mnt, dentry, &stat); if (!err && dev_mynode(dev, dentry->d_inode, &stat)) { struct iattr newattrs; /* * before unlinking this node, reset permissions * of possible references like hardlinks */ newattrs.ia_uid = GLOBAL_ROOT_UID; newattrs.ia_gid = GLOBAL_ROOT_GID; newattrs.ia_mode = stat.mode & ~0777; newattrs.ia_valid = ATTR_UID|ATTR_GID|ATTR_MODE; mutex_lock(&dentry->d_inode->i_mutex); notify_change(dentry, &newattrs); mutex_unlock(&dentry->d_inode->i_mutex); err = vfs_unlink(parent.dentry->d_inode, dentry); if (!err || err == -ENOENT) deleted = 1; } } else { err = -ENOENT; } dput(dentry); mutex_unlock(&parent.dentry->d_inode->i_mutex); path_put(&parent); if (deleted && strchr(nodename, '/')) delete_path(nodename); return err; } /* * If configured, or requested by the commandline, devtmpfs will be * auto-mounted after the kernel mounted the root filesystem. */ int devtmpfs_mount(const char *mntdir) { int err; if (!mount_dev) return 0; if (!thread) return 0; err = sys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, NULL); if (err) printk(KERN_INFO "devtmpfs: error mounting %i\n", err); else printk(KERN_INFO "devtmpfs: mounted\n"); return err; } static DECLARE_COMPLETION(setup_done); static int handle(const char *name, umode_t mode, struct device *dev) { if (mode) return handle_create(name, mode, dev); 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) { struct req *next = req->next; req->err = handle(req->name, req->mode, req->dev); complete(&req->done); req = next; } spin_lock(&req_lock); } __set_current_state(TASK_INTERRUPTIBLE); spin_unlock(&req_lock); schedule(); } return 0; out: complete(&setup_done); return *err; } /* * Create devtmpfs instance, driver-core devices will add their device * nodes here. */ int __init devtmpfs_init(void) { int err = register_filesystem(&dev_fs_type); if (err) { printk(KERN_ERR "devtmpfs: unable to register devtmpfs " "type %i\n", err); return err; } thread = kthread_run(devtmpfsd, &err, "kdevtmpfs"); if (!IS_ERR(thread)) { wait_for_completion(&setup_done); } else { err = PTR_ERR(thread); thread = NULL; } if (err) { printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); unregister_filesystem(&dev_fs_type); return err; } printk(KERN_INFO "devtmpfs: initialized\n"); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
You can’t perform that action at this time.