xref: /openbmc/linux/fs/proc/proc_sysctl.c (revision 77b14db5)
177b14db5SEric W. Biederman /*
277b14db5SEric W. Biederman  * /proc/sys support
377b14db5SEric W. Biederman  */
477b14db5SEric W. Biederman 
577b14db5SEric W. Biederman #include <linux/sysctl.h>
677b14db5SEric W. Biederman #include <linux/proc_fs.h>
777b14db5SEric W. Biederman #include <linux/security.h>
877b14db5SEric W. Biederman #include "internal.h"
977b14db5SEric W. Biederman 
1077b14db5SEric W. Biederman static struct dentry_operations proc_sys_dentry_operations;
1177b14db5SEric W. Biederman static const struct file_operations proc_sys_file_operations;
1277b14db5SEric W. Biederman static struct inode_operations proc_sys_inode_operations;
1377b14db5SEric W. Biederman 
1477b14db5SEric W. Biederman static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table)
1577b14db5SEric W. Biederman {
1677b14db5SEric W. Biederman 	/* Refresh the cached information bits in the inode */
1777b14db5SEric W. Biederman 	if (table) {
1877b14db5SEric W. Biederman 		inode->i_uid = 0;
1977b14db5SEric W. Biederman 		inode->i_gid = 0;
2077b14db5SEric W. Biederman 		inode->i_mode = table->mode;
2177b14db5SEric W. Biederman 		if (table->proc_handler) {
2277b14db5SEric W. Biederman 			inode->i_mode |= S_IFREG;
2377b14db5SEric W. Biederman 			inode->i_nlink = 1;
2477b14db5SEric W. Biederman 		} else {
2577b14db5SEric W. Biederman 			inode->i_mode |= S_IFDIR;
2677b14db5SEric W. Biederman 			inode->i_nlink = 0;	/* It is too hard to figure out */
2777b14db5SEric W. Biederman 		}
2877b14db5SEric W. Biederman 	}
2977b14db5SEric W. Biederman }
3077b14db5SEric W. Biederman 
3177b14db5SEric W. Biederman static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table)
3277b14db5SEric W. Biederman {
3377b14db5SEric W. Biederman 	struct inode *inode;
3477b14db5SEric W. Biederman 	struct proc_inode *dir_ei, *ei;
3577b14db5SEric W. Biederman 	int depth;
3677b14db5SEric W. Biederman 
3777b14db5SEric W. Biederman 	inode = new_inode(dir->i_sb);
3877b14db5SEric W. Biederman 	if (!inode)
3977b14db5SEric W. Biederman 		goto out;
4077b14db5SEric W. Biederman 
4177b14db5SEric W. Biederman 	/* A directory is always one deeper than it's parent */
4277b14db5SEric W. Biederman 	dir_ei = PROC_I(dir);
4377b14db5SEric W. Biederman 	depth = dir_ei->fd + 1;
4477b14db5SEric W. Biederman 
4577b14db5SEric W. Biederman 	ei = PROC_I(inode);
4677b14db5SEric W. Biederman 	ei->fd = depth;
4777b14db5SEric W. Biederman 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
4877b14db5SEric W. Biederman 	inode->i_op = &proc_sys_inode_operations;
4977b14db5SEric W. Biederman 	inode->i_fop = &proc_sys_file_operations;
5077b14db5SEric W. Biederman 	proc_sys_refresh_inode(inode, table);
5177b14db5SEric W. Biederman out:
5277b14db5SEric W. Biederman 	return inode;
5377b14db5SEric W. Biederman }
5477b14db5SEric W. Biederman 
5577b14db5SEric W. Biederman static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth)
5677b14db5SEric W. Biederman {
5777b14db5SEric W. Biederman 	for (;;) {
5877b14db5SEric W. Biederman 		struct proc_inode *ei;
5977b14db5SEric W. Biederman 
6077b14db5SEric W. Biederman 		ei = PROC_I(dentry->d_inode);
6177b14db5SEric W. Biederman 		if (ei->fd == depth)
6277b14db5SEric W. Biederman 			break; /* found */
6377b14db5SEric W. Biederman 
6477b14db5SEric W. Biederman 		dentry = dentry->d_parent;
6577b14db5SEric W. Biederman 	}
6677b14db5SEric W. Biederman 	return dentry;
6777b14db5SEric W. Biederman }
6877b14db5SEric W. Biederman 
6977b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table,
7077b14db5SEric W. Biederman 							struct qstr *name)
7177b14db5SEric W. Biederman {
7277b14db5SEric W. Biederman 	int len;
7377b14db5SEric W. Biederman 	for ( ; table->ctl_name || table->procname; table++) {
7477b14db5SEric W. Biederman 
7577b14db5SEric W. Biederman 		if (!table->procname)
7677b14db5SEric W. Biederman 			continue;
7777b14db5SEric W. Biederman 
7877b14db5SEric W. Biederman 		len = strlen(table->procname);
7977b14db5SEric W. Biederman 		if (len != name->len)
8077b14db5SEric W. Biederman 			continue;
8177b14db5SEric W. Biederman 
8277b14db5SEric W. Biederman 		if (memcmp(table->procname, name->name, len) != 0)
8377b14db5SEric W. Biederman 			continue;
8477b14db5SEric W. Biederman 
8577b14db5SEric W. Biederman 		/* I have a match */
8677b14db5SEric W. Biederman 		return table;
8777b14db5SEric W. Biederman 	}
8877b14db5SEric W. Biederman 	return NULL;
8977b14db5SEric W. Biederman }
9077b14db5SEric W. Biederman 
9177b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry,
9277b14db5SEric W. Biederman 						struct ctl_table *table)
9377b14db5SEric W. Biederman {
9477b14db5SEric W. Biederman 	struct dentry *ancestor;
9577b14db5SEric W. Biederman 	struct proc_inode *ei;
9677b14db5SEric W. Biederman 	int depth, i;
9777b14db5SEric W. Biederman 
9877b14db5SEric W. Biederman 	ei = PROC_I(dentry->d_inode);
9977b14db5SEric W. Biederman 	depth = ei->fd;
10077b14db5SEric W. Biederman 
10177b14db5SEric W. Biederman 	if (depth == 0)
10277b14db5SEric W. Biederman 		return table;
10377b14db5SEric W. Biederman 
10477b14db5SEric W. Biederman 	for (i = 1; table && (i <= depth); i++) {
10577b14db5SEric W. Biederman 		ancestor = proc_sys_ancestor(dentry, i);
10677b14db5SEric W. Biederman 		table = proc_sys_lookup_table_one(table, &ancestor->d_name);
10777b14db5SEric W. Biederman 		if (table)
10877b14db5SEric W. Biederman 			table = table->child;
10977b14db5SEric W. Biederman 	}
11077b14db5SEric W. Biederman 	return table;
11177b14db5SEric W. Biederman 
11277b14db5SEric W. Biederman }
11377b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent,
11477b14db5SEric W. Biederman 						struct qstr *name,
11577b14db5SEric W. Biederman 						struct ctl_table *table)
11677b14db5SEric W. Biederman {
11777b14db5SEric W. Biederman 	table = proc_sys_lookup_table(dparent, table);
11877b14db5SEric W. Biederman 	if (table)
11977b14db5SEric W. Biederman 		table = proc_sys_lookup_table_one(table, name);
12077b14db5SEric W. Biederman 	return table;
12177b14db5SEric W. Biederman }
12277b14db5SEric W. Biederman 
12377b14db5SEric W. Biederman static struct ctl_table *do_proc_sys_lookup(struct dentry *parent,
12477b14db5SEric W. Biederman 						struct qstr *name,
12577b14db5SEric W. Biederman 						struct ctl_table_header **ptr)
12677b14db5SEric W. Biederman {
12777b14db5SEric W. Biederman 	struct ctl_table_header *head;
12877b14db5SEric W. Biederman 	struct ctl_table *table = NULL;
12977b14db5SEric W. Biederman 
13077b14db5SEric W. Biederman 	for (head = sysctl_head_next(NULL); head;
13177b14db5SEric W. Biederman 			head = sysctl_head_next(head)) {
13277b14db5SEric W. Biederman 		table = proc_sys_lookup_entry(parent, name, head->ctl_table);
13377b14db5SEric W. Biederman 		if (table)
13477b14db5SEric W. Biederman 			break;
13577b14db5SEric W. Biederman 	}
13677b14db5SEric W. Biederman 	*ptr = head;
13777b14db5SEric W. Biederman 	return table;
13877b14db5SEric W. Biederman }
13977b14db5SEric W. Biederman 
14077b14db5SEric W. Biederman static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
14177b14db5SEric W. Biederman 					struct nameidata *nd)
14277b14db5SEric W. Biederman {
14377b14db5SEric W. Biederman 	struct ctl_table_header *head;
14477b14db5SEric W. Biederman 	struct inode *inode;
14577b14db5SEric W. Biederman 	struct dentry *err;
14677b14db5SEric W. Biederman 	struct ctl_table *table;
14777b14db5SEric W. Biederman 
14877b14db5SEric W. Biederman 	err = ERR_PTR(-ENOENT);
14977b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
15077b14db5SEric W. Biederman 	if (!table)
15177b14db5SEric W. Biederman 		goto out;
15277b14db5SEric W. Biederman 
15377b14db5SEric W. Biederman 	err = ERR_PTR(-ENOMEM);
15477b14db5SEric W. Biederman 	inode = proc_sys_make_inode(dir, table);
15577b14db5SEric W. Biederman 	if (!inode)
15677b14db5SEric W. Biederman 		goto out;
15777b14db5SEric W. Biederman 
15877b14db5SEric W. Biederman 	err = NULL;
15977b14db5SEric W. Biederman 	dentry->d_op = &proc_sys_dentry_operations;
16077b14db5SEric W. Biederman 	d_add(dentry, inode);
16177b14db5SEric W. Biederman 
16277b14db5SEric W. Biederman out:
16377b14db5SEric W. Biederman 	sysctl_head_finish(head);
16477b14db5SEric W. Biederman 	return err;
16577b14db5SEric W. Biederman }
16677b14db5SEric W. Biederman 
16777b14db5SEric W. Biederman static ssize_t proc_sys_read(struct file *filp, char __user *buf,
16877b14db5SEric W. Biederman 				size_t count, loff_t *ppos)
16977b14db5SEric W. Biederman {
17077b14db5SEric W. Biederman 	struct dentry *dentry = filp->f_dentry;
17177b14db5SEric W. Biederman 	struct ctl_table_header *head;
17277b14db5SEric W. Biederman 	struct ctl_table *table;
17377b14db5SEric W. Biederman 	ssize_t error, res;
17477b14db5SEric W. Biederman 
17577b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
17677b14db5SEric W. Biederman 	/* Has the sysctl entry disappeared on us? */
17777b14db5SEric W. Biederman 	error = -ENOENT;
17877b14db5SEric W. Biederman 	if (!table)
17977b14db5SEric W. Biederman 		goto out;
18077b14db5SEric W. Biederman 
18177b14db5SEric W. Biederman 	/* Has the sysctl entry been replaced by a directory? */
18277b14db5SEric W. Biederman 	error = -EISDIR;
18377b14db5SEric W. Biederman 	if (!table->proc_handler)
18477b14db5SEric W. Biederman 		goto out;
18577b14db5SEric W. Biederman 
18677b14db5SEric W. Biederman 	/*
18777b14db5SEric W. Biederman 	 * At this point we know that the sysctl was not unregistered
18877b14db5SEric W. Biederman 	 * and won't be until we finish.
18977b14db5SEric W. Biederman 	 */
19077b14db5SEric W. Biederman 	error = -EPERM;
19177b14db5SEric W. Biederman 	if (sysctl_perm(table, MAY_READ))
19277b14db5SEric W. Biederman 		goto out;
19377b14db5SEric W. Biederman 
19477b14db5SEric W. Biederman 	/* careful: calling conventions are nasty here */
19577b14db5SEric W. Biederman 	res = count;
19677b14db5SEric W. Biederman 	error = table->proc_handler(table, 0, filp, buf, &res, ppos);
19777b14db5SEric W. Biederman 	if (!error)
19877b14db5SEric W. Biederman 		error = res;
19977b14db5SEric W. Biederman out:
20077b14db5SEric W. Biederman 	sysctl_head_finish(head);
20177b14db5SEric W. Biederman 
20277b14db5SEric W. Biederman 	return error;
20377b14db5SEric W. Biederman }
20477b14db5SEric W. Biederman 
20577b14db5SEric W. Biederman static ssize_t proc_sys_write(struct file *filp, const char __user *buf,
20677b14db5SEric W. Biederman 				size_t count, loff_t *ppos)
20777b14db5SEric W. Biederman {
20877b14db5SEric W. Biederman 	struct dentry *dentry = filp->f_dentry;
20977b14db5SEric W. Biederman 	struct ctl_table_header *head;
21077b14db5SEric W. Biederman 	struct ctl_table *table;
21177b14db5SEric W. Biederman 	ssize_t error, res;
21277b14db5SEric W. Biederman 
21377b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
21477b14db5SEric W. Biederman 	/* Has the sysctl entry disappeared on us? */
21577b14db5SEric W. Biederman 	error = -ENOENT;
21677b14db5SEric W. Biederman 	if (!table)
21777b14db5SEric W. Biederman 		goto out;
21877b14db5SEric W. Biederman 
21977b14db5SEric W. Biederman 	/* Has the sysctl entry been replaced by a directory? */
22077b14db5SEric W. Biederman 	error = -EISDIR;
22177b14db5SEric W. Biederman 	if (!table->proc_handler)
22277b14db5SEric W. Biederman 		goto out;
22377b14db5SEric W. Biederman 
22477b14db5SEric W. Biederman 	/*
22577b14db5SEric W. Biederman 	 * At this point we know that the sysctl was not unregistered
22677b14db5SEric W. Biederman 	 * and won't be until we finish.
22777b14db5SEric W. Biederman 	 */
22877b14db5SEric W. Biederman 	error = -EPERM;
22977b14db5SEric W. Biederman 	if (sysctl_perm(table, MAY_WRITE))
23077b14db5SEric W. Biederman 		goto out;
23177b14db5SEric W. Biederman 
23277b14db5SEric W. Biederman 	/* careful: calling conventions are nasty here */
23377b14db5SEric W. Biederman 	res = count;
23477b14db5SEric W. Biederman 	error = table->proc_handler(table, 1, filp, (char __user *)buf,
23577b14db5SEric W. Biederman 				    &res, ppos);
23677b14db5SEric W. Biederman 	if (!error)
23777b14db5SEric W. Biederman 		error = res;
23877b14db5SEric W. Biederman out:
23977b14db5SEric W. Biederman 	sysctl_head_finish(head);
24077b14db5SEric W. Biederman 
24177b14db5SEric W. Biederman 	return error;
24277b14db5SEric W. Biederman }
24377b14db5SEric W. Biederman 
24477b14db5SEric W. Biederman 
24577b14db5SEric W. Biederman static int proc_sys_fill_cache(struct file *filp, void *dirent,
24677b14db5SEric W. Biederman 				filldir_t filldir, struct ctl_table *table)
24777b14db5SEric W. Biederman {
24877b14db5SEric W. Biederman 	struct ctl_table_header *head;
24977b14db5SEric W. Biederman 	struct ctl_table *child_table = NULL;
25077b14db5SEric W. Biederman 	struct dentry *child, *dir = filp->f_path.dentry;
25177b14db5SEric W. Biederman 	struct inode *inode;
25277b14db5SEric W. Biederman 	struct qstr qname;
25377b14db5SEric W. Biederman 	ino_t ino = 0;
25477b14db5SEric W. Biederman 	unsigned type = DT_UNKNOWN;
25577b14db5SEric W. Biederman 	int ret;
25677b14db5SEric W. Biederman 
25777b14db5SEric W. Biederman 	qname.name = table->procname;
25877b14db5SEric W. Biederman 	qname.len  = strlen(table->procname);
25977b14db5SEric W. Biederman 	qname.hash = full_name_hash(qname.name, qname.len);
26077b14db5SEric W. Biederman 
26177b14db5SEric W. Biederman 	/* Suppress duplicates.
26277b14db5SEric W. Biederman 	 * Only fill a directory entry if it is the value that
26377b14db5SEric W. Biederman 	 * an ordinary lookup of that name returns.  Hide all
26477b14db5SEric W. Biederman 	 * others.
26577b14db5SEric W. Biederman 	 *
26677b14db5SEric W. Biederman 	 * If we ever cache this translation in the dcache
26777b14db5SEric W. Biederman 	 * I should do a dcache lookup first.  But for now
26877b14db5SEric W. Biederman 	 * it is just simpler not to.
26977b14db5SEric W. Biederman 	 */
27077b14db5SEric W. Biederman 	ret = 0;
27177b14db5SEric W. Biederman 	child_table = do_proc_sys_lookup(dir, &qname, &head);
27277b14db5SEric W. Biederman 	sysctl_head_finish(head);
27377b14db5SEric W. Biederman 	if (child_table != table)
27477b14db5SEric W. Biederman 		return 0;
27577b14db5SEric W. Biederman 
27677b14db5SEric W. Biederman 	child = d_lookup(dir, &qname);
27777b14db5SEric W. Biederman 	if (!child) {
27877b14db5SEric W. Biederman 		struct dentry *new;
27977b14db5SEric W. Biederman 		new = d_alloc(dir, &qname);
28077b14db5SEric W. Biederman 		if (new) {
28177b14db5SEric W. Biederman 			inode = proc_sys_make_inode(dir->d_inode, table);
28277b14db5SEric W. Biederman 			if (!inode)
28377b14db5SEric W. Biederman 				child = ERR_PTR(-ENOMEM);
28477b14db5SEric W. Biederman 			else {
28577b14db5SEric W. Biederman 				new->d_op = &proc_sys_dentry_operations;
28677b14db5SEric W. Biederman 				d_add(new, inode);
28777b14db5SEric W. Biederman 			}
28877b14db5SEric W. Biederman 			if (child)
28977b14db5SEric W. Biederman 				dput(new);
29077b14db5SEric W. Biederman 			else
29177b14db5SEric W. Biederman 				child = new;
29277b14db5SEric W. Biederman 		}
29377b14db5SEric W. Biederman 	}
29477b14db5SEric W. Biederman 	if (!child || IS_ERR(child) || !child->d_inode)
29577b14db5SEric W. Biederman 		goto end_instantiate;
29677b14db5SEric W. Biederman 	inode = child->d_inode;
29777b14db5SEric W. Biederman 	if (inode) {
29877b14db5SEric W. Biederman 		ino  = inode->i_ino;
29977b14db5SEric W. Biederman 		type = inode->i_mode >> 12;
30077b14db5SEric W. Biederman 	}
30177b14db5SEric W. Biederman 	dput(child);
30277b14db5SEric W. Biederman end_instantiate:
30377b14db5SEric W. Biederman 	if (!ino)
30477b14db5SEric W. Biederman 		ino= find_inode_number(dir, &qname);
30577b14db5SEric W. Biederman 	if (!ino)
30677b14db5SEric W. Biederman 		ino = 1;
30777b14db5SEric W. Biederman 	return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
30877b14db5SEric W. Biederman }
30977b14db5SEric W. Biederman 
31077b14db5SEric W. Biederman static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
31177b14db5SEric W. Biederman {
31277b14db5SEric W. Biederman 	struct dentry *dentry = filp->f_dentry;
31377b14db5SEric W. Biederman 	struct inode *inode = dentry->d_inode;
31477b14db5SEric W. Biederman 	struct ctl_table_header *head = NULL;
31577b14db5SEric W. Biederman 	struct ctl_table *table;
31677b14db5SEric W. Biederman 	unsigned long pos;
31777b14db5SEric W. Biederman 	int ret;
31877b14db5SEric W. Biederman 
31977b14db5SEric W. Biederman 	ret = -ENOTDIR;
32077b14db5SEric W. Biederman 	if (!S_ISDIR(inode->i_mode))
32177b14db5SEric W. Biederman 		goto out;
32277b14db5SEric W. Biederman 
32377b14db5SEric W. Biederman 	ret = 0;
32477b14db5SEric W. Biederman 	/* Avoid a switch here: arm builds fail with missing __cmpdi2 */
32577b14db5SEric W. Biederman 	if (filp->f_pos == 0) {
32677b14db5SEric W. Biederman 		if (filldir(dirent, ".", 1, filp->f_pos,
32777b14db5SEric W. Biederman 				inode->i_ino, DT_DIR) < 0)
32877b14db5SEric W. Biederman 			goto out;
32977b14db5SEric W. Biederman 		filp->f_pos++;
33077b14db5SEric W. Biederman 	}
33177b14db5SEric W. Biederman 	if (filp->f_pos == 1) {
33277b14db5SEric W. Biederman 		if (filldir(dirent, "..", 2, filp->f_pos,
33377b14db5SEric W. Biederman 				parent_ino(dentry), DT_DIR) < 0)
33477b14db5SEric W. Biederman 			goto out;
33577b14db5SEric W. Biederman 		filp->f_pos++;
33677b14db5SEric W. Biederman 	}
33777b14db5SEric W. Biederman 	pos = 2;
33877b14db5SEric W. Biederman 
33977b14db5SEric W. Biederman 	/* - Find each instance of the directory
34077b14db5SEric W. Biederman 	 * - Read all entries in each instance
34177b14db5SEric W. Biederman 	 * - Before returning an entry to user space lookup the entry
34277b14db5SEric W. Biederman 	 *   by name and if I find a different entry don't return
34377b14db5SEric W. Biederman 	 *   this one because it means it is a buried dup.
34477b14db5SEric W. Biederman 	 * For sysctl this should only happen for directory entries.
34577b14db5SEric W. Biederman 	 */
34677b14db5SEric W. Biederman 	for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {
34777b14db5SEric W. Biederman 		table = proc_sys_lookup_table(dentry, head->ctl_table);
34877b14db5SEric W. Biederman 
34977b14db5SEric W. Biederman 		if (!table)
35077b14db5SEric W. Biederman 			continue;
35177b14db5SEric W. Biederman 
35277b14db5SEric W. Biederman 		for (; table->ctl_name || table->procname; table++, pos++) {
35377b14db5SEric W. Biederman 			/* Can't do anything without a proc name */
35477b14db5SEric W. Biederman 			if (!table->procname)
35577b14db5SEric W. Biederman 				continue;
35677b14db5SEric W. Biederman 
35777b14db5SEric W. Biederman 			if (pos < filp->f_pos)
35877b14db5SEric W. Biederman 				continue;
35977b14db5SEric W. Biederman 
36077b14db5SEric W. Biederman 			if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0)
36177b14db5SEric W. Biederman 				goto out;
36277b14db5SEric W. Biederman 			filp->f_pos = pos + 1;
36377b14db5SEric W. Biederman 		}
36477b14db5SEric W. Biederman 	}
36577b14db5SEric W. Biederman 	ret = 1;
36677b14db5SEric W. Biederman out:
36777b14db5SEric W. Biederman 	sysctl_head_finish(head);
36877b14db5SEric W. Biederman 	return ret;
36977b14db5SEric W. Biederman }
37077b14db5SEric W. Biederman 
37177b14db5SEric W. Biederman static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd)
37277b14db5SEric W. Biederman {
37377b14db5SEric W. Biederman 	/*
37477b14db5SEric W. Biederman 	 * sysctl entries that are not writeable,
37577b14db5SEric W. Biederman 	 * are _NOT_ writeable, capabilities or not.
37677b14db5SEric W. Biederman 	 */
37777b14db5SEric W. Biederman 	struct ctl_table_header *head;
37877b14db5SEric W. Biederman 	struct ctl_table *table;
37977b14db5SEric W. Biederman 	struct dentry *dentry;
38077b14db5SEric W. Biederman 	int mode;
38177b14db5SEric W. Biederman 	int depth;
38277b14db5SEric W. Biederman 	int error;
38377b14db5SEric W. Biederman 
38477b14db5SEric W. Biederman 	head = NULL;
38577b14db5SEric W. Biederman 	depth = PROC_I(inode)->fd;
38677b14db5SEric W. Biederman 
38777b14db5SEric W. Biederman 	/* First check the cached permissions, in case we don't have
38877b14db5SEric W. Biederman 	 * enough information to lookup the sysctl table entry.
38977b14db5SEric W. Biederman 	 */
39077b14db5SEric W. Biederman 	error = -EACCES;
39177b14db5SEric W. Biederman 	mode = inode->i_mode;
39277b14db5SEric W. Biederman 
39377b14db5SEric W. Biederman 	if (current->euid == 0)
39477b14db5SEric W. Biederman 		mode >>= 6;
39577b14db5SEric W. Biederman 	else if (in_group_p(0))
39677b14db5SEric W. Biederman 		mode >>= 3;
39777b14db5SEric W. Biederman 
39877b14db5SEric W. Biederman 	if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)
39977b14db5SEric W. Biederman 		error = 0;
40077b14db5SEric W. Biederman 
40177b14db5SEric W. Biederman 	/* If we can't get a sysctl table entry the permission
40277b14db5SEric W. Biederman 	 * checks on the cached mode will have to be enough.
40377b14db5SEric W. Biederman 	 */
40477b14db5SEric W. Biederman 	if (!nd || !depth)
40577b14db5SEric W. Biederman 		goto out;
40677b14db5SEric W. Biederman 
40777b14db5SEric W. Biederman 	dentry = nd->dentry;
40877b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
40977b14db5SEric W. Biederman 
41077b14db5SEric W. Biederman 	/* If the entry does not exist deny permission */
41177b14db5SEric W. Biederman 	error = -EACCES;
41277b14db5SEric W. Biederman 	if (!table)
41377b14db5SEric W. Biederman 		goto out;
41477b14db5SEric W. Biederman 
41577b14db5SEric W. Biederman 	/* Use the permissions on the sysctl table entry */
41677b14db5SEric W. Biederman 	error = sysctl_perm(table, mask);
41777b14db5SEric W. Biederman out:
41877b14db5SEric W. Biederman 	sysctl_head_finish(head);
41977b14db5SEric W. Biederman 	return error;
42077b14db5SEric W. Biederman }
42177b14db5SEric W. Biederman 
42277b14db5SEric W. Biederman static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
42377b14db5SEric W. Biederman {
42477b14db5SEric W. Biederman 	struct inode *inode = dentry->d_inode;
42577b14db5SEric W. Biederman 	int error;
42677b14db5SEric W. Biederman 
42777b14db5SEric W. Biederman 	if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
42877b14db5SEric W. Biederman 		return -EPERM;
42977b14db5SEric W. Biederman 
43077b14db5SEric W. Biederman 	error = inode_change_ok(inode, attr);
43177b14db5SEric W. Biederman 	if (!error) {
43277b14db5SEric W. Biederman 		error = security_inode_setattr(dentry, attr);
43377b14db5SEric W. Biederman 		if (!error)
43477b14db5SEric W. Biederman 			error = inode_setattr(inode, attr);
43577b14db5SEric W. Biederman 	}
43677b14db5SEric W. Biederman 
43777b14db5SEric W. Biederman 	return error;
43877b14db5SEric W. Biederman }
43977b14db5SEric W. Biederman 
44077b14db5SEric W. Biederman /* I'm lazy and don't distinguish between files and directories,
44177b14db5SEric W. Biederman  * until access time.
44277b14db5SEric W. Biederman  */
44377b14db5SEric W. Biederman static const struct file_operations proc_sys_file_operations = {
44477b14db5SEric W. Biederman 	.read		= proc_sys_read,
44577b14db5SEric W. Biederman 	.write		= proc_sys_write,
44677b14db5SEric W. Biederman 	.readdir	= proc_sys_readdir,
44777b14db5SEric W. Biederman };
44877b14db5SEric W. Biederman 
44977b14db5SEric W. Biederman static struct inode_operations proc_sys_inode_operations = {
45077b14db5SEric W. Biederman 	.lookup		= proc_sys_lookup,
45177b14db5SEric W. Biederman 	.permission	= proc_sys_permission,
45277b14db5SEric W. Biederman 	.setattr	= proc_sys_setattr,
45377b14db5SEric W. Biederman };
45477b14db5SEric W. Biederman 
45577b14db5SEric W. Biederman static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd)
45677b14db5SEric W. Biederman {
45777b14db5SEric W. Biederman 	struct ctl_table_header *head;
45877b14db5SEric W. Biederman 	struct ctl_table *table;
45977b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
46077b14db5SEric W. Biederman 	proc_sys_refresh_inode(dentry->d_inode, table);
46177b14db5SEric W. Biederman 	sysctl_head_finish(head);
46277b14db5SEric W. Biederman 	return !!table;
46377b14db5SEric W. Biederman }
46477b14db5SEric W. Biederman 
46577b14db5SEric W. Biederman static struct dentry_operations proc_sys_dentry_operations = {
46677b14db5SEric W. Biederman 	.d_revalidate	= proc_sys_revalidate,
46777b14db5SEric W. Biederman };
46877b14db5SEric W. Biederman 
46977b14db5SEric W. Biederman static struct proc_dir_entry *proc_sys_root;
47077b14db5SEric W. Biederman 
47177b14db5SEric W. Biederman int proc_sys_init(void)
47277b14db5SEric W. Biederman {
47377b14db5SEric W. Biederman 	proc_sys_root = proc_mkdir("sys", NULL);
47477b14db5SEric W. Biederman 	proc_sys_root->proc_iops = &proc_sys_inode_operations;
47577b14db5SEric W. Biederman 	proc_sys_root->proc_fops = &proc_sys_file_operations;
47677b14db5SEric W. Biederman 	proc_sys_root->nlink = 0;
47777b14db5SEric W. Biederman 	return 0;
47877b14db5SEric W. Biederman }
479