xref: /openbmc/linux/fs/proc/proc_sysctl.c (revision 03a44825)
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;
1203a44825SJan Engelhardt static const 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;
5086a71dbdSEric W. Biederman 	inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */
5177b14db5SEric W. Biederman 	proc_sys_refresh_inode(inode, table);
5277b14db5SEric W. Biederman out:
5377b14db5SEric W. Biederman 	return inode;
5477b14db5SEric W. Biederman }
5577b14db5SEric W. Biederman 
5677b14db5SEric W. Biederman static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth)
5777b14db5SEric W. Biederman {
5877b14db5SEric W. Biederman 	for (;;) {
5977b14db5SEric W. Biederman 		struct proc_inode *ei;
6077b14db5SEric W. Biederman 
6177b14db5SEric W. Biederman 		ei = PROC_I(dentry->d_inode);
6277b14db5SEric W. Biederman 		if (ei->fd == depth)
6377b14db5SEric W. Biederman 			break; /* found */
6477b14db5SEric W. Biederman 
6577b14db5SEric W. Biederman 		dentry = dentry->d_parent;
6677b14db5SEric W. Biederman 	}
6777b14db5SEric W. Biederman 	return dentry;
6877b14db5SEric W. Biederman }
6977b14db5SEric W. Biederman 
7077b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table,
7177b14db5SEric W. Biederman 							struct qstr *name)
7277b14db5SEric W. Biederman {
7377b14db5SEric W. Biederman 	int len;
7477b14db5SEric W. Biederman 	for ( ; table->ctl_name || table->procname; table++) {
7577b14db5SEric W. Biederman 
7677b14db5SEric W. Biederman 		if (!table->procname)
7777b14db5SEric W. Biederman 			continue;
7877b14db5SEric W. Biederman 
7977b14db5SEric W. Biederman 		len = strlen(table->procname);
8077b14db5SEric W. Biederman 		if (len != name->len)
8177b14db5SEric W. Biederman 			continue;
8277b14db5SEric W. Biederman 
8377b14db5SEric W. Biederman 		if (memcmp(table->procname, name->name, len) != 0)
8477b14db5SEric W. Biederman 			continue;
8577b14db5SEric W. Biederman 
8677b14db5SEric W. Biederman 		/* I have a match */
8777b14db5SEric W. Biederman 		return table;
8877b14db5SEric W. Biederman 	}
8977b14db5SEric W. Biederman 	return NULL;
9077b14db5SEric W. Biederman }
9177b14db5SEric W. Biederman 
9277b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry,
9377b14db5SEric W. Biederman 						struct ctl_table *table)
9477b14db5SEric W. Biederman {
9577b14db5SEric W. Biederman 	struct dentry *ancestor;
9677b14db5SEric W. Biederman 	struct proc_inode *ei;
9777b14db5SEric W. Biederman 	int depth, i;
9877b14db5SEric W. Biederman 
9977b14db5SEric W. Biederman 	ei = PROC_I(dentry->d_inode);
10077b14db5SEric W. Biederman 	depth = ei->fd;
10177b14db5SEric W. Biederman 
10277b14db5SEric W. Biederman 	if (depth == 0)
10377b14db5SEric W. Biederman 		return table;
10477b14db5SEric W. Biederman 
10577b14db5SEric W. Biederman 	for (i = 1; table && (i <= depth); i++) {
10677b14db5SEric W. Biederman 		ancestor = proc_sys_ancestor(dentry, i);
10777b14db5SEric W. Biederman 		table = proc_sys_lookup_table_one(table, &ancestor->d_name);
10877b14db5SEric W. Biederman 		if (table)
10977b14db5SEric W. Biederman 			table = table->child;
11077b14db5SEric W. Biederman 	}
11177b14db5SEric W. Biederman 	return table;
11277b14db5SEric W. Biederman 
11377b14db5SEric W. Biederman }
11477b14db5SEric W. Biederman static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent,
11577b14db5SEric W. Biederman 						struct qstr *name,
11677b14db5SEric W. Biederman 						struct ctl_table *table)
11777b14db5SEric W. Biederman {
11877b14db5SEric W. Biederman 	table = proc_sys_lookup_table(dparent, table);
11977b14db5SEric W. Biederman 	if (table)
12077b14db5SEric W. Biederman 		table = proc_sys_lookup_table_one(table, name);
12177b14db5SEric W. Biederman 	return table;
12277b14db5SEric W. Biederman }
12377b14db5SEric W. Biederman 
12477b14db5SEric W. Biederman static struct ctl_table *do_proc_sys_lookup(struct dentry *parent,
12577b14db5SEric W. Biederman 						struct qstr *name,
12677b14db5SEric W. Biederman 						struct ctl_table_header **ptr)
12777b14db5SEric W. Biederman {
12877b14db5SEric W. Biederman 	struct ctl_table_header *head;
12977b14db5SEric W. Biederman 	struct ctl_table *table = NULL;
13077b14db5SEric W. Biederman 
13177b14db5SEric W. Biederman 	for (head = sysctl_head_next(NULL); head;
13277b14db5SEric W. Biederman 			head = sysctl_head_next(head)) {
13377b14db5SEric W. Biederman 		table = proc_sys_lookup_entry(parent, name, head->ctl_table);
13477b14db5SEric W. Biederman 		if (table)
13577b14db5SEric W. Biederman 			break;
13677b14db5SEric W. Biederman 	}
13777b14db5SEric W. Biederman 	*ptr = head;
13877b14db5SEric W. Biederman 	return table;
13977b14db5SEric W. Biederman }
14077b14db5SEric W. Biederman 
14177b14db5SEric W. Biederman static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
14277b14db5SEric W. Biederman 					struct nameidata *nd)
14377b14db5SEric W. Biederman {
14477b14db5SEric W. Biederman 	struct ctl_table_header *head;
14577b14db5SEric W. Biederman 	struct inode *inode;
14677b14db5SEric W. Biederman 	struct dentry *err;
14777b14db5SEric W. Biederman 	struct ctl_table *table;
14877b14db5SEric W. Biederman 
14977b14db5SEric W. Biederman 	err = ERR_PTR(-ENOENT);
15077b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
15177b14db5SEric W. Biederman 	if (!table)
15277b14db5SEric W. Biederman 		goto out;
15377b14db5SEric W. Biederman 
15477b14db5SEric W. Biederman 	err = ERR_PTR(-ENOMEM);
15577b14db5SEric W. Biederman 	inode = proc_sys_make_inode(dir, table);
15677b14db5SEric W. Biederman 	if (!inode)
15777b14db5SEric W. Biederman 		goto out;
15877b14db5SEric W. Biederman 
15977b14db5SEric W. Biederman 	err = NULL;
16077b14db5SEric W. Biederman 	dentry->d_op = &proc_sys_dentry_operations;
16177b14db5SEric W. Biederman 	d_add(dentry, inode);
16277b14db5SEric W. Biederman 
16377b14db5SEric W. Biederman out:
16477b14db5SEric W. Biederman 	sysctl_head_finish(head);
16577b14db5SEric W. Biederman 	return err;
16677b14db5SEric W. Biederman }
16777b14db5SEric W. Biederman 
16877b14db5SEric W. Biederman static ssize_t proc_sys_read(struct file *filp, char __user *buf,
16977b14db5SEric W. Biederman 				size_t count, loff_t *ppos)
17077b14db5SEric W. Biederman {
17177b14db5SEric W. Biederman 	struct dentry *dentry = filp->f_dentry;
17277b14db5SEric W. Biederman 	struct ctl_table_header *head;
17377b14db5SEric W. Biederman 	struct ctl_table *table;
1742a2da53bSDavid Howells 	ssize_t error;
1752a2da53bSDavid Howells 	size_t res;
17677b14db5SEric W. Biederman 
17777b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
17877b14db5SEric W. Biederman 	/* Has the sysctl entry disappeared on us? */
17977b14db5SEric W. Biederman 	error = -ENOENT;
18077b14db5SEric W. Biederman 	if (!table)
18177b14db5SEric W. Biederman 		goto out;
18277b14db5SEric W. Biederman 
18377b14db5SEric W. Biederman 	/* Has the sysctl entry been replaced by a directory? */
18477b14db5SEric W. Biederman 	error = -EISDIR;
18577b14db5SEric W. Biederman 	if (!table->proc_handler)
18677b14db5SEric W. Biederman 		goto out;
18777b14db5SEric W. Biederman 
18877b14db5SEric W. Biederman 	/*
18977b14db5SEric W. Biederman 	 * At this point we know that the sysctl was not unregistered
19077b14db5SEric W. Biederman 	 * and won't be until we finish.
19177b14db5SEric W. Biederman 	 */
19277b14db5SEric W. Biederman 	error = -EPERM;
19377b14db5SEric W. Biederman 	if (sysctl_perm(table, MAY_READ))
19477b14db5SEric W. Biederman 		goto out;
19577b14db5SEric W. Biederman 
19677b14db5SEric W. Biederman 	/* careful: calling conventions are nasty here */
19777b14db5SEric W. Biederman 	res = count;
19877b14db5SEric W. Biederman 	error = table->proc_handler(table, 0, filp, buf, &res, ppos);
19977b14db5SEric W. Biederman 	if (!error)
20077b14db5SEric W. Biederman 		error = res;
20177b14db5SEric W. Biederman out:
20277b14db5SEric W. Biederman 	sysctl_head_finish(head);
20377b14db5SEric W. Biederman 
20477b14db5SEric W. Biederman 	return error;
20577b14db5SEric W. Biederman }
20677b14db5SEric W. Biederman 
20777b14db5SEric W. Biederman static ssize_t proc_sys_write(struct file *filp, const char __user *buf,
20877b14db5SEric W. Biederman 				size_t count, loff_t *ppos)
20977b14db5SEric W. Biederman {
21077b14db5SEric W. Biederman 	struct dentry *dentry = filp->f_dentry;
21177b14db5SEric W. Biederman 	struct ctl_table_header *head;
21277b14db5SEric W. Biederman 	struct ctl_table *table;
2132a2da53bSDavid Howells 	ssize_t error;
2142a2da53bSDavid Howells 	size_t res;
21577b14db5SEric W. Biederman 
21677b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
21777b14db5SEric W. Biederman 	/* Has the sysctl entry disappeared on us? */
21877b14db5SEric W. Biederman 	error = -ENOENT;
21977b14db5SEric W. Biederman 	if (!table)
22077b14db5SEric W. Biederman 		goto out;
22177b14db5SEric W. Biederman 
22277b14db5SEric W. Biederman 	/* Has the sysctl entry been replaced by a directory? */
22377b14db5SEric W. Biederman 	error = -EISDIR;
22477b14db5SEric W. Biederman 	if (!table->proc_handler)
22577b14db5SEric W. Biederman 		goto out;
22677b14db5SEric W. Biederman 
22777b14db5SEric W. Biederman 	/*
22877b14db5SEric W. Biederman 	 * At this point we know that the sysctl was not unregistered
22977b14db5SEric W. Biederman 	 * and won't be until we finish.
23077b14db5SEric W. Biederman 	 */
23177b14db5SEric W. Biederman 	error = -EPERM;
23277b14db5SEric W. Biederman 	if (sysctl_perm(table, MAY_WRITE))
23377b14db5SEric W. Biederman 		goto out;
23477b14db5SEric W. Biederman 
23577b14db5SEric W. Biederman 	/* careful: calling conventions are nasty here */
23677b14db5SEric W. Biederman 	res = count;
23777b14db5SEric W. Biederman 	error = table->proc_handler(table, 1, filp, (char __user *)buf,
23877b14db5SEric W. Biederman 				    &res, ppos);
23977b14db5SEric W. Biederman 	if (!error)
24077b14db5SEric W. Biederman 		error = res;
24177b14db5SEric W. Biederman out:
24277b14db5SEric W. Biederman 	sysctl_head_finish(head);
24377b14db5SEric W. Biederman 
24477b14db5SEric W. Biederman 	return error;
24577b14db5SEric W. Biederman }
24677b14db5SEric W. Biederman 
24777b14db5SEric W. Biederman 
24877b14db5SEric W. Biederman static int proc_sys_fill_cache(struct file *filp, void *dirent,
24977b14db5SEric W. Biederman 				filldir_t filldir, struct ctl_table *table)
25077b14db5SEric W. Biederman {
25177b14db5SEric W. Biederman 	struct ctl_table_header *head;
25277b14db5SEric W. Biederman 	struct ctl_table *child_table = NULL;
25377b14db5SEric W. Biederman 	struct dentry *child, *dir = filp->f_path.dentry;
25477b14db5SEric W. Biederman 	struct inode *inode;
25577b14db5SEric W. Biederman 	struct qstr qname;
25677b14db5SEric W. Biederman 	ino_t ino = 0;
25777b14db5SEric W. Biederman 	unsigned type = DT_UNKNOWN;
25877b14db5SEric W. Biederman 	int ret;
25977b14db5SEric W. Biederman 
26077b14db5SEric W. Biederman 	qname.name = table->procname;
26177b14db5SEric W. Biederman 	qname.len  = strlen(table->procname);
26277b14db5SEric W. Biederman 	qname.hash = full_name_hash(qname.name, qname.len);
26377b14db5SEric W. Biederman 
26477b14db5SEric W. Biederman 	/* Suppress duplicates.
26577b14db5SEric W. Biederman 	 * Only fill a directory entry if it is the value that
26677b14db5SEric W. Biederman 	 * an ordinary lookup of that name returns.  Hide all
26777b14db5SEric W. Biederman 	 * others.
26877b14db5SEric W. Biederman 	 *
26977b14db5SEric W. Biederman 	 * If we ever cache this translation in the dcache
27077b14db5SEric W. Biederman 	 * I should do a dcache lookup first.  But for now
27177b14db5SEric W. Biederman 	 * it is just simpler not to.
27277b14db5SEric W. Biederman 	 */
27377b14db5SEric W. Biederman 	ret = 0;
27477b14db5SEric W. Biederman 	child_table = do_proc_sys_lookup(dir, &qname, &head);
27577b14db5SEric W. Biederman 	sysctl_head_finish(head);
27677b14db5SEric W. Biederman 	if (child_table != table)
27777b14db5SEric W. Biederman 		return 0;
27877b14db5SEric W. Biederman 
27977b14db5SEric W. Biederman 	child = d_lookup(dir, &qname);
28077b14db5SEric W. Biederman 	if (!child) {
28177b14db5SEric W. Biederman 		struct dentry *new;
28277b14db5SEric W. Biederman 		new = d_alloc(dir, &qname);
28377b14db5SEric W. Biederman 		if (new) {
28477b14db5SEric W. Biederman 			inode = proc_sys_make_inode(dir->d_inode, table);
28577b14db5SEric W. Biederman 			if (!inode)
28677b14db5SEric W. Biederman 				child = ERR_PTR(-ENOMEM);
28777b14db5SEric W. Biederman 			else {
28877b14db5SEric W. Biederman 				new->d_op = &proc_sys_dentry_operations;
28977b14db5SEric W. Biederman 				d_add(new, inode);
29077b14db5SEric W. Biederman 			}
29177b14db5SEric W. Biederman 			if (child)
29277b14db5SEric W. Biederman 				dput(new);
29377b14db5SEric W. Biederman 			else
29477b14db5SEric W. Biederman 				child = new;
29577b14db5SEric W. Biederman 		}
29677b14db5SEric W. Biederman 	}
29777b14db5SEric W. Biederman 	if (!child || IS_ERR(child) || !child->d_inode)
29877b14db5SEric W. Biederman 		goto end_instantiate;
29977b14db5SEric W. Biederman 	inode = child->d_inode;
30077b14db5SEric W. Biederman 	if (inode) {
30177b14db5SEric W. Biederman 		ino  = inode->i_ino;
30277b14db5SEric W. Biederman 		type = inode->i_mode >> 12;
30377b14db5SEric W. Biederman 	}
30477b14db5SEric W. Biederman 	dput(child);
30577b14db5SEric W. Biederman end_instantiate:
30677b14db5SEric W. Biederman 	if (!ino)
30777b14db5SEric W. Biederman 		ino= find_inode_number(dir, &qname);
30877b14db5SEric W. Biederman 	if (!ino)
30977b14db5SEric W. Biederman 		ino = 1;
31077b14db5SEric W. Biederman 	return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
31177b14db5SEric W. Biederman }
31277b14db5SEric W. Biederman 
31377b14db5SEric W. Biederman static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
31477b14db5SEric W. Biederman {
31577b14db5SEric W. Biederman 	struct dentry *dentry = filp->f_dentry;
31677b14db5SEric W. Biederman 	struct inode *inode = dentry->d_inode;
31777b14db5SEric W. Biederman 	struct ctl_table_header *head = NULL;
31877b14db5SEric W. Biederman 	struct ctl_table *table;
31977b14db5SEric W. Biederman 	unsigned long pos;
32077b14db5SEric W. Biederman 	int ret;
32177b14db5SEric W. Biederman 
32277b14db5SEric W. Biederman 	ret = -ENOTDIR;
32377b14db5SEric W. Biederman 	if (!S_ISDIR(inode->i_mode))
32477b14db5SEric W. Biederman 		goto out;
32577b14db5SEric W. Biederman 
32677b14db5SEric W. Biederman 	ret = 0;
32777b14db5SEric W. Biederman 	/* Avoid a switch here: arm builds fail with missing __cmpdi2 */
32877b14db5SEric W. Biederman 	if (filp->f_pos == 0) {
32977b14db5SEric W. Biederman 		if (filldir(dirent, ".", 1, filp->f_pos,
33077b14db5SEric W. Biederman 				inode->i_ino, DT_DIR) < 0)
33177b14db5SEric W. Biederman 			goto out;
33277b14db5SEric W. Biederman 		filp->f_pos++;
33377b14db5SEric W. Biederman 	}
33477b14db5SEric W. Biederman 	if (filp->f_pos == 1) {
33577b14db5SEric W. Biederman 		if (filldir(dirent, "..", 2, filp->f_pos,
33677b14db5SEric W. Biederman 				parent_ino(dentry), DT_DIR) < 0)
33777b14db5SEric W. Biederman 			goto out;
33877b14db5SEric W. Biederman 		filp->f_pos++;
33977b14db5SEric W. Biederman 	}
34077b14db5SEric W. Biederman 	pos = 2;
34177b14db5SEric W. Biederman 
34277b14db5SEric W. Biederman 	/* - Find each instance of the directory
34377b14db5SEric W. Biederman 	 * - Read all entries in each instance
34477b14db5SEric W. Biederman 	 * - Before returning an entry to user space lookup the entry
34577b14db5SEric W. Biederman 	 *   by name and if I find a different entry don't return
34677b14db5SEric W. Biederman 	 *   this one because it means it is a buried dup.
34777b14db5SEric W. Biederman 	 * For sysctl this should only happen for directory entries.
34877b14db5SEric W. Biederman 	 */
34977b14db5SEric W. Biederman 	for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {
35077b14db5SEric W. Biederman 		table = proc_sys_lookup_table(dentry, head->ctl_table);
35177b14db5SEric W. Biederman 
35277b14db5SEric W. Biederman 		if (!table)
35377b14db5SEric W. Biederman 			continue;
35477b14db5SEric W. Biederman 
35577b14db5SEric W. Biederman 		for (; table->ctl_name || table->procname; table++, pos++) {
35677b14db5SEric W. Biederman 			/* Can't do anything without a proc name */
35777b14db5SEric W. Biederman 			if (!table->procname)
35877b14db5SEric W. Biederman 				continue;
35977b14db5SEric W. Biederman 
36077b14db5SEric W. Biederman 			if (pos < filp->f_pos)
36177b14db5SEric W. Biederman 				continue;
36277b14db5SEric W. Biederman 
36377b14db5SEric W. Biederman 			if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0)
36477b14db5SEric W. Biederman 				goto out;
36577b14db5SEric W. Biederman 			filp->f_pos = pos + 1;
36677b14db5SEric W. Biederman 		}
36777b14db5SEric W. Biederman 	}
36877b14db5SEric W. Biederman 	ret = 1;
36977b14db5SEric W. Biederman out:
37077b14db5SEric W. Biederman 	sysctl_head_finish(head);
37177b14db5SEric W. Biederman 	return ret;
37277b14db5SEric W. Biederman }
37377b14db5SEric W. Biederman 
37477b14db5SEric W. Biederman static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd)
37577b14db5SEric W. Biederman {
37677b14db5SEric W. Biederman 	/*
37777b14db5SEric W. Biederman 	 * sysctl entries that are not writeable,
37877b14db5SEric W. Biederman 	 * are _NOT_ writeable, capabilities or not.
37977b14db5SEric W. Biederman 	 */
38077b14db5SEric W. Biederman 	struct ctl_table_header *head;
38177b14db5SEric W. Biederman 	struct ctl_table *table;
38277b14db5SEric W. Biederman 	struct dentry *dentry;
38377b14db5SEric W. Biederman 	int mode;
38477b14db5SEric W. Biederman 	int depth;
38577b14db5SEric W. Biederman 	int error;
38677b14db5SEric W. Biederman 
38777b14db5SEric W. Biederman 	head = NULL;
38877b14db5SEric W. Biederman 	depth = PROC_I(inode)->fd;
38977b14db5SEric W. Biederman 
39077b14db5SEric W. Biederman 	/* First check the cached permissions, in case we don't have
39177b14db5SEric W. Biederman 	 * enough information to lookup the sysctl table entry.
39277b14db5SEric W. Biederman 	 */
39377b14db5SEric W. Biederman 	error = -EACCES;
39477b14db5SEric W. Biederman 	mode = inode->i_mode;
39577b14db5SEric W. Biederman 
39677b14db5SEric W. Biederman 	if (current->euid == 0)
39777b14db5SEric W. Biederman 		mode >>= 6;
39877b14db5SEric W. Biederman 	else if (in_group_p(0))
39977b14db5SEric W. Biederman 		mode >>= 3;
40077b14db5SEric W. Biederman 
40177b14db5SEric W. Biederman 	if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)
40277b14db5SEric W. Biederman 		error = 0;
40377b14db5SEric W. Biederman 
40477b14db5SEric W. Biederman 	/* If we can't get a sysctl table entry the permission
40577b14db5SEric W. Biederman 	 * checks on the cached mode will have to be enough.
40677b14db5SEric W. Biederman 	 */
40777b14db5SEric W. Biederman 	if (!nd || !depth)
40877b14db5SEric W. Biederman 		goto out;
40977b14db5SEric W. Biederman 
41077b14db5SEric W. Biederman 	dentry = nd->dentry;
41177b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
41277b14db5SEric W. Biederman 
41377b14db5SEric W. Biederman 	/* If the entry does not exist deny permission */
41477b14db5SEric W. Biederman 	error = -EACCES;
41577b14db5SEric W. Biederman 	if (!table)
41677b14db5SEric W. Biederman 		goto out;
41777b14db5SEric W. Biederman 
41877b14db5SEric W. Biederman 	/* Use the permissions on the sysctl table entry */
41977b14db5SEric W. Biederman 	error = sysctl_perm(table, mask);
42077b14db5SEric W. Biederman out:
42177b14db5SEric W. Biederman 	sysctl_head_finish(head);
42277b14db5SEric W. Biederman 	return error;
42377b14db5SEric W. Biederman }
42477b14db5SEric W. Biederman 
42577b14db5SEric W. Biederman static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
42677b14db5SEric W. Biederman {
42777b14db5SEric W. Biederman 	struct inode *inode = dentry->d_inode;
42877b14db5SEric W. Biederman 	int error;
42977b14db5SEric W. Biederman 
43077b14db5SEric W. Biederman 	if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
43177b14db5SEric W. Biederman 		return -EPERM;
43277b14db5SEric W. Biederman 
43377b14db5SEric W. Biederman 	error = inode_change_ok(inode, attr);
43477b14db5SEric W. Biederman 	if (!error)
43577b14db5SEric W. Biederman 		error = inode_setattr(inode, attr);
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 
44903a44825SJan Engelhardt static const 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