⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 proc_sysctl.c

📁 linux 内核源代码
💻 C
字号:
/* * /proc/sys support */#include <linux/sysctl.h>#include <linux/proc_fs.h>#include <linux/security.h>#include "internal.h"static struct dentry_operations proc_sys_dentry_operations;static const struct file_operations proc_sys_file_operations;static struct inode_operations proc_sys_inode_operations;static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table){	/* Refresh the cached information bits in the inode */	if (table) {		inode->i_uid = 0;		inode->i_gid = 0;		inode->i_mode = table->mode;		if (table->proc_handler) {			inode->i_mode |= S_IFREG;			inode->i_nlink = 1;		} else {			inode->i_mode |= S_IFDIR;			inode->i_nlink = 0;	/* It is too hard to figure out */		}	}}static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table){	struct inode *inode;	struct proc_inode *dir_ei, *ei;	int depth;	inode = new_inode(dir->i_sb);	if (!inode)		goto out;	/* A directory is always one deeper than it's parent */	dir_ei = PROC_I(dir);	depth = dir_ei->fd + 1;	ei = PROC_I(inode);	ei->fd = depth;	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;	inode->i_op = &proc_sys_inode_operations;	inode->i_fop = &proc_sys_file_operations;	inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */	proc_sys_refresh_inode(inode, table);out:	return inode;}static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth){	for (;;) {		struct proc_inode *ei;		ei = PROC_I(dentry->d_inode);		if (ei->fd == depth)			break; /* found */		dentry = dentry->d_parent;	}	return dentry;}static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table,							struct qstr *name){	int len;	for ( ; table->ctl_name || table->procname; table++) {		if (!table->procname)			continue;		len = strlen(table->procname);		if (len != name->len)			continue;		if (memcmp(table->procname, name->name, len) != 0)			continue;		/* I have a match */		return table;	}	return NULL;}static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry,						struct ctl_table *table){	struct dentry *ancestor;	struct proc_inode *ei;	int depth, i;	ei = PROC_I(dentry->d_inode);	depth = ei->fd;	if (depth == 0)		return table;	for (i = 1; table && (i <= depth); i++) {		ancestor = proc_sys_ancestor(dentry, i);		table = proc_sys_lookup_table_one(table, &ancestor->d_name);		if (table)			table = table->child;	}	return table;}static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent,						struct qstr *name,						struct ctl_table *table){	table = proc_sys_lookup_table(dparent, table);	if (table)		table = proc_sys_lookup_table_one(table, name);	return table;}static struct ctl_table *do_proc_sys_lookup(struct dentry *parent,						struct qstr *name,						struct ctl_table_header **ptr){	struct ctl_table_header *head;	struct ctl_table *table = NULL;	for (head = sysctl_head_next(NULL); head;			head = sysctl_head_next(head)) {		table = proc_sys_lookup_entry(parent, name, head->ctl_table);		if (table)			break;	}	*ptr = head;	return table;}static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,					struct nameidata *nd){	struct ctl_table_header *head;	struct inode *inode;	struct dentry *err;	struct ctl_table *table;	err = ERR_PTR(-ENOENT);	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);	if (!table)		goto out;	err = ERR_PTR(-ENOMEM);	inode = proc_sys_make_inode(dir, table);	if (!inode)		goto out;	err = NULL;	dentry->d_op = &proc_sys_dentry_operations;	d_add(dentry, inode);out:	sysctl_head_finish(head);	return err;}static ssize_t proc_sys_read(struct file *filp, char __user *buf,				size_t count, loff_t *ppos){	struct dentry *dentry = filp->f_dentry;	struct ctl_table_header *head;	struct ctl_table *table;	ssize_t error;	size_t res;	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);	/* Has the sysctl entry disappeared on us? */	error = -ENOENT;	if (!table)		goto out;	/* Has the sysctl entry been replaced by a directory? */	error = -EISDIR;	if (!table->proc_handler)		goto out;	/*	 * At this point we know that the sysctl was not unregistered	 * and won't be until we finish.	 */	error = -EPERM;	if (sysctl_perm(table, MAY_READ))		goto out;	/* careful: calling conventions are nasty here */	res = count;	error = table->proc_handler(table, 0, filp, buf, &res, ppos);	if (!error)		error = res;out:	sysctl_head_finish(head);	return error;}static ssize_t proc_sys_write(struct file *filp, const char __user *buf,				size_t count, loff_t *ppos){	struct dentry *dentry = filp->f_dentry;	struct ctl_table_header *head;	struct ctl_table *table;	ssize_t error;	size_t res;	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);	/* Has the sysctl entry disappeared on us? */	error = -ENOENT;	if (!table)		goto out;	/* Has the sysctl entry been replaced by a directory? */	error = -EISDIR;	if (!table->proc_handler)		goto out;	/*	 * At this point we know that the sysctl was not unregistered	 * and won't be until we finish.	 */	error = -EPERM;	if (sysctl_perm(table, MAY_WRITE))		goto out;	/* careful: calling conventions are nasty here */	res = count;	error = table->proc_handler(table, 1, filp, (char __user *)buf,				    &res, ppos);	if (!error)		error = res;out:	sysctl_head_finish(head);	return error;}static int proc_sys_fill_cache(struct file *filp, void *dirent,				filldir_t filldir, struct ctl_table *table){	struct ctl_table_header *head;	struct ctl_table *child_table = NULL;	struct dentry *child, *dir = filp->f_path.dentry;	struct inode *inode;	struct qstr qname;	ino_t ino = 0;	unsigned type = DT_UNKNOWN;	int ret;	qname.name = table->procname;	qname.len  = strlen(table->procname);	qname.hash = full_name_hash(qname.name, qname.len);	/* Suppress duplicates.	 * Only fill a directory entry if it is the value that	 * an ordinary lookup of that name returns.  Hide all	 * others.	 *	 * If we ever cache this translation in the dcache	 * I should do a dcache lookup first.  But for now	 * it is just simpler not to.	 */	ret = 0;	child_table = do_proc_sys_lookup(dir, &qname, &head);	sysctl_head_finish(head);	if (child_table != table)		return 0;	child = d_lookup(dir, &qname);	if (!child) {		struct dentry *new;		new = d_alloc(dir, &qname);		if (new) {			inode = proc_sys_make_inode(dir->d_inode, table);			if (!inode)				child = ERR_PTR(-ENOMEM);			else {				new->d_op = &proc_sys_dentry_operations;				d_add(new, inode);			}			if (child)				dput(new);			else				child = new;		}	}	if (!child || IS_ERR(child) || !child->d_inode)		goto end_instantiate;	inode = child->d_inode;	if (inode) {		ino  = inode->i_ino;		type = inode->i_mode >> 12;	}	dput(child);end_instantiate:	if (!ino)		ino= find_inode_number(dir, &qname);	if (!ino)		ino = 1;	return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);}static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir){	struct dentry *dentry = filp->f_dentry;	struct inode *inode = dentry->d_inode;	struct ctl_table_header *head = NULL;	struct ctl_table *table;	unsigned long pos;	int ret;	ret = -ENOTDIR;	if (!S_ISDIR(inode->i_mode))		goto out;	ret = 0;	/* Avoid a switch here: arm builds fail with missing __cmpdi2 */	if (filp->f_pos == 0) {		if (filldir(dirent, ".", 1, filp->f_pos,				inode->i_ino, DT_DIR) < 0)			goto out;		filp->f_pos++;	}	if (filp->f_pos == 1) {		if (filldir(dirent, "..", 2, filp->f_pos,				parent_ino(dentry), DT_DIR) < 0)			goto out;		filp->f_pos++;	}	pos = 2;	/* - Find each instance of the directory	 * - Read all entries in each instance	 * - Before returning an entry to user space lookup the entry	 *   by name and if I find a different entry don't return	 *   this one because it means it is a buried dup.	 * For sysctl this should only happen for directory entries.	 */	for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {		table = proc_sys_lookup_table(dentry, head->ctl_table);		if (!table)			continue;		for (; table->ctl_name || table->procname; table++, pos++) {			/* Can't do anything without a proc name */			if (!table->procname)				continue;			if (pos < filp->f_pos)				continue;			if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0)				goto out;			filp->f_pos = pos + 1;		}	}	ret = 1;out:	sysctl_head_finish(head);	return ret;}static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd){	/*	 * sysctl entries that are not writeable,	 * are _NOT_ writeable, capabilities or not.	 */	struct ctl_table_header *head;	struct ctl_table *table;	struct dentry *dentry;	int mode;	int depth;	int error;	head = NULL;	depth = PROC_I(inode)->fd;	/* First check the cached permissions, in case we don't have	 * enough information to lookup the sysctl table entry.	 */	error = -EACCES;	mode = inode->i_mode;	if (current->euid == 0)		mode >>= 6;	else if (in_group_p(0))		mode >>= 3;	if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)		error = 0;	/* If we can't get a sysctl table entry the permission	 * checks on the cached mode will have to be enough.	 */	if (!nd || !depth)		goto out;	dentry = nd->dentry;	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);	/* If the entry does not exist deny permission */	error = -EACCES;	if (!table)		goto out;	/* Use the permissions on the sysctl table entry */	error = sysctl_perm(table, mask);out:	sysctl_head_finish(head);	return error;}static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr){	struct inode *inode = dentry->d_inode;	int error;	if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))		return -EPERM;	error = inode_change_ok(inode, attr);	if (!error)		error = inode_setattr(inode, attr);	return error;}/* I'm lazy and don't distinguish between files and directories, * until access time. */static const struct file_operations proc_sys_file_operations = {	.read		= proc_sys_read,	.write		= proc_sys_write,	.readdir	= proc_sys_readdir,};static struct inode_operations proc_sys_inode_operations = {	.lookup		= proc_sys_lookup,	.permission	= proc_sys_permission,	.setattr	= proc_sys_setattr,};static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd){	struct ctl_table_header *head;	struct ctl_table *table;	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);	proc_sys_refresh_inode(dentry->d_inode, table);	sysctl_head_finish(head);	return !!table;}static struct dentry_operations proc_sys_dentry_operations = {	.d_revalidate	= proc_sys_revalidate,};static struct proc_dir_entry *proc_sys_root;int proc_sys_init(void){	proc_sys_root = proc_mkdir("sys", NULL);	proc_sys_root->proc_iops = &proc_sys_inode_operations;	proc_sys_root->proc_fops = &proc_sys_file_operations;	proc_sys_root->nlink = 0;	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -