cregit-Linux how code gets into the kernel

Release 4.14 drivers/base/devtmpfs.c

Directory: drivers/base
// SPDX-License-Identifier: GPL-2.0
/*
 * 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>
#include "base.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 */
	
kuid_t uid;
	
kgid_t gid;
	
struct device *dev;

} *requests;


static int __init mount_param(char *str) { mount_dev = simple_strtoul(str, NULL, 0); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers2496.00%150.00%
Al Viro14.00%150.00%
Total25100.00%2100.00%

__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 }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers3257.14%133.33%
Peter Korsgaard1832.14%133.33%
Al Viro610.71%133.33%
Total56100.00%3100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers20100.00%1100.00%
Total20100.00%1100.00%

#else
static inline int is_blockdev(struct device *dev) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers15100.00%1100.00%
Total15100.00%1100.00%

#endif
int devtmpfs_create_node(struct device *dev) { const char *tmp = NULL; struct req req; if (!thread) return 0; req.mode = 0; req.uid = GLOBAL_ROOT_UID; req.gid = GLOBAL_ROOT_GID; req.name = device_get_devnode(dev, &req.mode, &req.uid, &req.gid, &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; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro15787.71%133.33%
Kay Sievers2011.17%133.33%
Greg Kroah-Hartman21.12%133.33%
Total179100.00%3100.00%


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, NULL, 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; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro12096.77%150.00%
Kay Sievers43.23%150.00%
Total124100.00%2100.00%


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, LOOKUP_DIRECTORY); if (IS_ERR(dentry)) return PTR_ERR(dentry); err = vfs_mkdir(d_inode(path.dentry), dentry, mode); if (!err) /* mark as kernel-created inode */ d_inode(dentry)->i_private = &thread; done_path_create(&path, dentry); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers7072.92%225.00%
Al Viro1919.79%450.00%
David Howells66.25%112.50%
Jeff Layton11.04%112.50%
Total96100.00%8100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers10693.81%250.00%
Al Viro76.19%250.00%
Total113100.00%4100.00%


static int handle_create(const char *nodename, umode_t mode, kuid_t uid, kgid_t gid, 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(d_inode(path.dentry), dentry, mode, dev->devt); if (!err) { struct iattr newattrs; newattrs.ia_mode = mode; newattrs.ia_uid = uid; newattrs.ia_gid = gid; newattrs.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID; inode_lock(d_inode(dentry)); notify_change(dentry, &newattrs, NULL); inode_unlock(d_inode(dentry)); /* mark as kernel-created inode */ d_inode(dentry)->i_private = &thread; } done_path_create(&path, dentry); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers14370.79%327.27%
Al Viro4321.29%545.45%
David Howells125.94%19.09%
J. Bruce Fields20.99%19.09%
Greg Kroah-Hartman20.99%19.09%
Total202100.00%11100.00%


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 (d_really_is_positive(dentry)) { if (d_inode(dentry)->i_private == &thread) err = vfs_rmdir(d_inode(parent.dentry), dentry); else err = -EPERM; } else { err = -ENOENT; } dput(dentry); inode_unlock(d_inode(parent.dentry)); path_put(&parent); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers9277.31%233.33%
Al Viro1512.61%350.00%
David Howells1210.08%116.67%
Total119100.00%6100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers92100.00%2100.00%
Total92100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers8998.89%150.00%
Al Viro11.11%150.00%
Total90100.00%2100.00%


static int handle_remove(const char *nodename, struct device *dev) { struct path parent; struct dentry *dentry; int deleted = 0; int err; dentry = kern_path_locked(nodename, &parent); if (IS_ERR(dentry)) return PTR_ERR(dentry); if (d_really_is_positive(dentry)) { struct kstat stat; struct path p = {.mnt = parent.mnt, .dentry = dentry}; err = vfs_getattr(&p, &stat, STATX_TYPE | STATX_MODE, AT_STATX_SYNC_AS_STAT); if (!err && dev_mynode(dev, d_inode(dentry), &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; inode_lock(d_inode(dentry)); notify_change(dentry, &newattrs, NULL); inode_unlock(d_inode(dentry)); err = vfs_unlink(d_inode(parent.dentry), dentry, NULL); if (!err || err == -ENOENT) deleted = 1; } } else { err = -ENOENT; } dput(dentry); inode_unlock(d_inode(parent.dentry)); path_put(&parent); if (deleted && strchr(nodename, '/')) delete_path(nodename); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers18669.92%216.67%
Al Viro4918.42%433.33%
David Howells249.02%216.67%
J. Bruce Fields41.50%216.67%
Eric W. Biedermann20.75%18.33%
Axel Lin10.38%18.33%
Total266100.00%12100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers6897.14%250.00%
Al Viro22.86%250.00%
Total70100.00%4100.00%

static DECLARE_COMPLETION(setup_done);
static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) { if (mode) return handle_create(name, mode, uid, gid, dev); else return handle_remove(name, dev); }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro3873.08%240.00%
Kay Sievers1223.08%240.00%
Greg Kroah-Hartman23.85%120.00%
Total52100.00%5100.00%


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->uid, req->gid, 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; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro18490.64%233.33%
Kay Sievers125.91%233.33%
Heiko Carstens62.96%116.67%
Kautuk Consul10.49%116.67%
Total203100.00%6100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers7064.22%266.67%
Al Viro3935.78%133.33%
Total109100.00%3100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers114457.60%923.68%
Al Viro72936.71%1231.58%
David Howells542.72%25.26%
Peter Korsgaard221.11%25.26%
Greg Kroah-Hartman120.60%37.89%
J. Bruce Fields60.30%25.26%
Heiko Carstens60.30%12.63%
Thomas Gleixner30.15%12.63%
Arnaud Lacombe30.15%12.63%
Tejun Heo20.10%12.63%
Eric W. Biedermann20.10%12.63%
Kautuk Consul10.05%12.63%
Axel Lin10.05%12.63%
Jeff Layton10.05%12.63%
Total1986100.00%38100.00%
Directory: drivers/base
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.