xref: /openbmc/linux/fs/proc/proc_sysctl.c (revision 7708bfb1)
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 
1687708bfb1SPavel Emelyanov static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf,
1697708bfb1SPavel Emelyanov 		size_t count, loff_t *ppos, int write)
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;
1937708bfb1SPavel Emelyanov 	if (sysctl_perm(table, write ? MAY_WRITE : MAY_READ))
19477b14db5SEric W. Biederman 		goto out;
19577b14db5SEric W. Biederman 
19677b14db5SEric W. Biederman 	/* careful: calling conventions are nasty here */
19777b14db5SEric W. Biederman 	res = count;
1987708bfb1SPavel Emelyanov 	error = table->proc_handler(table, write, 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 
2077708bfb1SPavel Emelyanov static ssize_t proc_sys_read(struct file *filp, char __user *buf,
2087708bfb1SPavel Emelyanov 				size_t count, loff_t *ppos)
2097708bfb1SPavel Emelyanov {
2107708bfb1SPavel Emelyanov 	return proc_sys_call_handler(filp, (void __user *)buf, count, ppos, 0);
2117708bfb1SPavel Emelyanov }
2127708bfb1SPavel Emelyanov 
21377b14db5SEric W. Biederman static ssize_t proc_sys_write(struct file *filp, const char __user *buf,
21477b14db5SEric W. Biederman 				size_t count, loff_t *ppos)
21577b14db5SEric W. Biederman {
2167708bfb1SPavel Emelyanov 	return proc_sys_call_handler(filp, (void __user *)buf, count, ppos, 1);
21777b14db5SEric W. Biederman }
21877b14db5SEric W. Biederman 
21977b14db5SEric W. Biederman 
22077b14db5SEric W. Biederman static int proc_sys_fill_cache(struct file *filp, void *dirent,
22177b14db5SEric W. Biederman 				filldir_t filldir, struct ctl_table *table)
22277b14db5SEric W. Biederman {
22377b14db5SEric W. Biederman 	struct ctl_table_header *head;
22477b14db5SEric W. Biederman 	struct ctl_table *child_table = NULL;
22577b14db5SEric W. Biederman 	struct dentry *child, *dir = filp->f_path.dentry;
22677b14db5SEric W. Biederman 	struct inode *inode;
22777b14db5SEric W. Biederman 	struct qstr qname;
22877b14db5SEric W. Biederman 	ino_t ino = 0;
22977b14db5SEric W. Biederman 	unsigned type = DT_UNKNOWN;
23077b14db5SEric W. Biederman 	int ret;
23177b14db5SEric W. Biederman 
23277b14db5SEric W. Biederman 	qname.name = table->procname;
23377b14db5SEric W. Biederman 	qname.len  = strlen(table->procname);
23477b14db5SEric W. Biederman 	qname.hash = full_name_hash(qname.name, qname.len);
23577b14db5SEric W. Biederman 
23677b14db5SEric W. Biederman 	/* Suppress duplicates.
23777b14db5SEric W. Biederman 	 * Only fill a directory entry if it is the value that
23877b14db5SEric W. Biederman 	 * an ordinary lookup of that name returns.  Hide all
23977b14db5SEric W. Biederman 	 * others.
24077b14db5SEric W. Biederman 	 *
24177b14db5SEric W. Biederman 	 * If we ever cache this translation in the dcache
24277b14db5SEric W. Biederman 	 * I should do a dcache lookup first.  But for now
24377b14db5SEric W. Biederman 	 * it is just simpler not to.
24477b14db5SEric W. Biederman 	 */
24577b14db5SEric W. Biederman 	ret = 0;
24677b14db5SEric W. Biederman 	child_table = do_proc_sys_lookup(dir, &qname, &head);
24777b14db5SEric W. Biederman 	sysctl_head_finish(head);
24877b14db5SEric W. Biederman 	if (child_table != table)
24977b14db5SEric W. Biederman 		return 0;
25077b14db5SEric W. Biederman 
25177b14db5SEric W. Biederman 	child = d_lookup(dir, &qname);
25277b14db5SEric W. Biederman 	if (!child) {
25377b14db5SEric W. Biederman 		struct dentry *new;
25477b14db5SEric W. Biederman 		new = d_alloc(dir, &qname);
25577b14db5SEric W. Biederman 		if (new) {
25677b14db5SEric W. Biederman 			inode = proc_sys_make_inode(dir->d_inode, table);
25777b14db5SEric W. Biederman 			if (!inode)
25877b14db5SEric W. Biederman 				child = ERR_PTR(-ENOMEM);
25977b14db5SEric W. Biederman 			else {
26077b14db5SEric W. Biederman 				new->d_op = &proc_sys_dentry_operations;
26177b14db5SEric W. Biederman 				d_add(new, inode);
26277b14db5SEric W. Biederman 			}
26377b14db5SEric W. Biederman 			if (child)
26477b14db5SEric W. Biederman 				dput(new);
26577b14db5SEric W. Biederman 			else
26677b14db5SEric W. Biederman 				child = new;
26777b14db5SEric W. Biederman 		}
26877b14db5SEric W. Biederman 	}
26977b14db5SEric W. Biederman 	if (!child || IS_ERR(child) || !child->d_inode)
27077b14db5SEric W. Biederman 		goto end_instantiate;
27177b14db5SEric W. Biederman 	inode = child->d_inode;
27277b14db5SEric W. Biederman 	if (inode) {
27377b14db5SEric W. Biederman 		ino  = inode->i_ino;
27477b14db5SEric W. Biederman 		type = inode->i_mode >> 12;
27577b14db5SEric W. Biederman 	}
27677b14db5SEric W. Biederman 	dput(child);
27777b14db5SEric W. Biederman end_instantiate:
27877b14db5SEric W. Biederman 	if (!ino)
27977b14db5SEric W. Biederman 		ino= find_inode_number(dir, &qname);
28077b14db5SEric W. Biederman 	if (!ino)
28177b14db5SEric W. Biederman 		ino = 1;
28277b14db5SEric W. Biederman 	return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
28377b14db5SEric W. Biederman }
28477b14db5SEric W. Biederman 
28577b14db5SEric W. Biederman static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
28677b14db5SEric W. Biederman {
28777b14db5SEric W. Biederman 	struct dentry *dentry = filp->f_dentry;
28877b14db5SEric W. Biederman 	struct inode *inode = dentry->d_inode;
28977b14db5SEric W. Biederman 	struct ctl_table_header *head = NULL;
29077b14db5SEric W. Biederman 	struct ctl_table *table;
29177b14db5SEric W. Biederman 	unsigned long pos;
29277b14db5SEric W. Biederman 	int ret;
29377b14db5SEric W. Biederman 
29477b14db5SEric W. Biederman 	ret = -ENOTDIR;
29577b14db5SEric W. Biederman 	if (!S_ISDIR(inode->i_mode))
29677b14db5SEric W. Biederman 		goto out;
29777b14db5SEric W. Biederman 
29877b14db5SEric W. Biederman 	ret = 0;
29977b14db5SEric W. Biederman 	/* Avoid a switch here: arm builds fail with missing __cmpdi2 */
30077b14db5SEric W. Biederman 	if (filp->f_pos == 0) {
30177b14db5SEric W. Biederman 		if (filldir(dirent, ".", 1, filp->f_pos,
30277b14db5SEric W. Biederman 				inode->i_ino, DT_DIR) < 0)
30377b14db5SEric W. Biederman 			goto out;
30477b14db5SEric W. Biederman 		filp->f_pos++;
30577b14db5SEric W. Biederman 	}
30677b14db5SEric W. Biederman 	if (filp->f_pos == 1) {
30777b14db5SEric W. Biederman 		if (filldir(dirent, "..", 2, filp->f_pos,
30877b14db5SEric W. Biederman 				parent_ino(dentry), DT_DIR) < 0)
30977b14db5SEric W. Biederman 			goto out;
31077b14db5SEric W. Biederman 		filp->f_pos++;
31177b14db5SEric W. Biederman 	}
31277b14db5SEric W. Biederman 	pos = 2;
31377b14db5SEric W. Biederman 
31477b14db5SEric W. Biederman 	/* - Find each instance of the directory
31577b14db5SEric W. Biederman 	 * - Read all entries in each instance
31677b14db5SEric W. Biederman 	 * - Before returning an entry to user space lookup the entry
31777b14db5SEric W. Biederman 	 *   by name and if I find a different entry don't return
31877b14db5SEric W. Biederman 	 *   this one because it means it is a buried dup.
31977b14db5SEric W. Biederman 	 * For sysctl this should only happen for directory entries.
32077b14db5SEric W. Biederman 	 */
32177b14db5SEric W. Biederman 	for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {
32277b14db5SEric W. Biederman 		table = proc_sys_lookup_table(dentry, head->ctl_table);
32377b14db5SEric W. Biederman 
32477b14db5SEric W. Biederman 		if (!table)
32577b14db5SEric W. Biederman 			continue;
32677b14db5SEric W. Biederman 
32777b14db5SEric W. Biederman 		for (; table->ctl_name || table->procname; table++, pos++) {
32877b14db5SEric W. Biederman 			/* Can't do anything without a proc name */
32977b14db5SEric W. Biederman 			if (!table->procname)
33077b14db5SEric W. Biederman 				continue;
33177b14db5SEric W. Biederman 
33277b14db5SEric W. Biederman 			if (pos < filp->f_pos)
33377b14db5SEric W. Biederman 				continue;
33477b14db5SEric W. Biederman 
33577b14db5SEric W. Biederman 			if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0)
33677b14db5SEric W. Biederman 				goto out;
33777b14db5SEric W. Biederman 			filp->f_pos = pos + 1;
33877b14db5SEric W. Biederman 		}
33977b14db5SEric W. Biederman 	}
34077b14db5SEric W. Biederman 	ret = 1;
34177b14db5SEric W. Biederman out:
34277b14db5SEric W. Biederman 	sysctl_head_finish(head);
34377b14db5SEric W. Biederman 	return ret;
34477b14db5SEric W. Biederman }
34577b14db5SEric W. Biederman 
34677b14db5SEric W. Biederman static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd)
34777b14db5SEric W. Biederman {
34877b14db5SEric W. Biederman 	/*
34977b14db5SEric W. Biederman 	 * sysctl entries that are not writeable,
35077b14db5SEric W. Biederman 	 * are _NOT_ writeable, capabilities or not.
35177b14db5SEric W. Biederman 	 */
35277b14db5SEric W. Biederman 	struct ctl_table_header *head;
35377b14db5SEric W. Biederman 	struct ctl_table *table;
35477b14db5SEric W. Biederman 	struct dentry *dentry;
35577b14db5SEric W. Biederman 	int mode;
35677b14db5SEric W. Biederman 	int depth;
35777b14db5SEric W. Biederman 	int error;
35877b14db5SEric W. Biederman 
35977b14db5SEric W. Biederman 	head = NULL;
36077b14db5SEric W. Biederman 	depth = PROC_I(inode)->fd;
36177b14db5SEric W. Biederman 
36277b14db5SEric W. Biederman 	/* First check the cached permissions, in case we don't have
36377b14db5SEric W. Biederman 	 * enough information to lookup the sysctl table entry.
36477b14db5SEric W. Biederman 	 */
36577b14db5SEric W. Biederman 	error = -EACCES;
36677b14db5SEric W. Biederman 	mode = inode->i_mode;
36777b14db5SEric W. Biederman 
36877b14db5SEric W. Biederman 	if (current->euid == 0)
36977b14db5SEric W. Biederman 		mode >>= 6;
37077b14db5SEric W. Biederman 	else if (in_group_p(0))
37177b14db5SEric W. Biederman 		mode >>= 3;
37277b14db5SEric W. Biederman 
37377b14db5SEric W. Biederman 	if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)
37477b14db5SEric W. Biederman 		error = 0;
37577b14db5SEric W. Biederman 
37677b14db5SEric W. Biederman 	/* If we can't get a sysctl table entry the permission
37777b14db5SEric W. Biederman 	 * checks on the cached mode will have to be enough.
37877b14db5SEric W. Biederman 	 */
37977b14db5SEric W. Biederman 	if (!nd || !depth)
38077b14db5SEric W. Biederman 		goto out;
38177b14db5SEric W. Biederman 
3824ac91378SJan Blunck 	dentry = nd->path.dentry;
38377b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
38477b14db5SEric W. Biederman 
38577b14db5SEric W. Biederman 	/* If the entry does not exist deny permission */
38677b14db5SEric W. Biederman 	error = -EACCES;
38777b14db5SEric W. Biederman 	if (!table)
38877b14db5SEric W. Biederman 		goto out;
38977b14db5SEric W. Biederman 
39077b14db5SEric W. Biederman 	/* Use the permissions on the sysctl table entry */
39177b14db5SEric W. Biederman 	error = sysctl_perm(table, mask);
39277b14db5SEric W. Biederman out:
39377b14db5SEric W. Biederman 	sysctl_head_finish(head);
39477b14db5SEric W. Biederman 	return error;
39577b14db5SEric W. Biederman }
39677b14db5SEric W. Biederman 
39777b14db5SEric W. Biederman static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
39877b14db5SEric W. Biederman {
39977b14db5SEric W. Biederman 	struct inode *inode = dentry->d_inode;
40077b14db5SEric W. Biederman 	int error;
40177b14db5SEric W. Biederman 
40277b14db5SEric W. Biederman 	if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
40377b14db5SEric W. Biederman 		return -EPERM;
40477b14db5SEric W. Biederman 
40577b14db5SEric W. Biederman 	error = inode_change_ok(inode, attr);
40677b14db5SEric W. Biederman 	if (!error)
40777b14db5SEric W. Biederman 		error = inode_setattr(inode, attr);
40877b14db5SEric W. Biederman 
40977b14db5SEric W. Biederman 	return error;
41077b14db5SEric W. Biederman }
41177b14db5SEric W. Biederman 
41277b14db5SEric W. Biederman /* I'm lazy and don't distinguish between files and directories,
41377b14db5SEric W. Biederman  * until access time.
41477b14db5SEric W. Biederman  */
41577b14db5SEric W. Biederman static const struct file_operations proc_sys_file_operations = {
41677b14db5SEric W. Biederman 	.read		= proc_sys_read,
41777b14db5SEric W. Biederman 	.write		= proc_sys_write,
41877b14db5SEric W. Biederman 	.readdir	= proc_sys_readdir,
41977b14db5SEric W. Biederman };
42077b14db5SEric W. Biederman 
42103a44825SJan Engelhardt static const struct inode_operations proc_sys_inode_operations = {
42277b14db5SEric W. Biederman 	.lookup		= proc_sys_lookup,
42377b14db5SEric W. Biederman 	.permission	= proc_sys_permission,
42477b14db5SEric W. Biederman 	.setattr	= proc_sys_setattr,
42577b14db5SEric W. Biederman };
42677b14db5SEric W. Biederman 
42777b14db5SEric W. Biederman static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd)
42877b14db5SEric W. Biederman {
42977b14db5SEric W. Biederman 	struct ctl_table_header *head;
43077b14db5SEric W. Biederman 	struct ctl_table *table;
43177b14db5SEric W. Biederman 	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
43277b14db5SEric W. Biederman 	proc_sys_refresh_inode(dentry->d_inode, table);
43377b14db5SEric W. Biederman 	sysctl_head_finish(head);
43477b14db5SEric W. Biederman 	return !!table;
43577b14db5SEric W. Biederman }
43677b14db5SEric W. Biederman 
43777b14db5SEric W. Biederman static struct dentry_operations proc_sys_dentry_operations = {
43877b14db5SEric W. Biederman 	.d_revalidate	= proc_sys_revalidate,
43977b14db5SEric W. Biederman };
44077b14db5SEric W. Biederman 
44177b14db5SEric W. Biederman static struct proc_dir_entry *proc_sys_root;
44277b14db5SEric W. Biederman 
44377b14db5SEric W. Biederman int proc_sys_init(void)
44477b14db5SEric W. Biederman {
44577b14db5SEric W. Biederman 	proc_sys_root = proc_mkdir("sys", NULL);
44677b14db5SEric W. Biederman 	proc_sys_root->proc_iops = &proc_sys_inode_operations;
44777b14db5SEric W. Biederman 	proc_sys_root->proc_fops = &proc_sys_file_operations;
44877b14db5SEric W. Biederman 	proc_sys_root->nlink = 0;
44977b14db5SEric W. Biederman 	return 0;
45077b14db5SEric W. Biederman }
451