proc_sysctl.c 9.6 KB
Newer Older
1 2 3
/*
 * /proc/sys support
 */
A
Alexey Dobriyan 已提交
4
#include <linux/init.h>
5 6 7 8 9
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/security.h>
#include "internal.h"

A
Al Viro 已提交
10
static const struct dentry_operations proc_sys_dentry_operations;
11
static const struct file_operations proc_sys_file_operations;
12
static const struct inode_operations proc_sys_inode_operations;
A
Al Viro 已提交
13 14
static const struct file_operations proc_sys_dir_file_operations;
static const struct inode_operations proc_sys_dir_operations;
15

A
Al Viro 已提交
16 17
static struct inode *proc_sys_make_inode(struct super_block *sb,
		struct ctl_table_header *head, struct ctl_table *table)
18 19
{
	struct inode *inode;
A
Al Viro 已提交
20
	struct proc_inode *ei;
21

A
Al Viro 已提交
22
	inode = new_inode(sb);
23 24 25
	if (!inode)
		goto out;

A
Al Viro 已提交
26
	sysctl_head_get(head);
27
	ei = PROC_I(inode);
A
Al Viro 已提交
28 29 30
	ei->sysctl = head;
	ei->sysctl_entry = table;

31
	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
32
	inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */
A
Al Viro 已提交
33 34 35 36 37 38 39 40 41 42 43
	inode->i_mode = table->mode;
	if (!table->child) {
		inode->i_mode |= S_IFREG;
		inode->i_op = &proc_sys_inode_operations;
		inode->i_fop = &proc_sys_file_operations;
	} else {
		inode->i_mode |= S_IFDIR;
		inode->i_nlink = 0;
		inode->i_op = &proc_sys_dir_operations;
		inode->i_fop = &proc_sys_dir_file_operations;
	}
44 45 46 47
out:
	return inode;
}

A
Al Viro 已提交
48
static struct ctl_table *find_in_table(struct ctl_table *p, struct qstr *name)
49 50
{
	int len;
A
Al Viro 已提交
51
	for ( ; p->ctl_name || p->procname; p++) {
52

A
Al Viro 已提交
53
		if (!p->procname)
54 55
			continue;

A
Al Viro 已提交
56
		len = strlen(p->procname);
57 58 59
		if (len != name->len)
			continue;

A
Al Viro 已提交
60
		if (memcmp(p->procname, name->name, len) != 0)
61 62 63
			continue;

		/* I have a match */
A
Al Viro 已提交
64
		return p;
65 66 67 68
	}
	return NULL;
}

A
Adrian Bunk 已提交
69
static struct ctl_table_header *grab_header(struct inode *inode)
70
{
A
Al Viro 已提交
71 72 73 74 75
	if (PROC_I(inode)->sysctl)
		return sysctl_head_grab(PROC_I(inode)->sysctl);
	else
		return sysctl_head_next(NULL);
}
76

A
Al Viro 已提交
77 78 79 80 81 82 83 84 85 86
static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
					struct nameidata *nd)
{
	struct ctl_table_header *head = grab_header(dir);
	struct ctl_table *table = PROC_I(dir)->sysctl_entry;
	struct ctl_table_header *h = NULL;
	struct qstr *name = &dentry->d_name;
	struct ctl_table *p;
	struct inode *inode;
	struct dentry *err = ERR_PTR(-ENOENT);
87

A
Al Viro 已提交
88 89
	if (IS_ERR(head))
		return ERR_CAST(head);
90

A
Al Viro 已提交
91 92 93
	if (table && !table->child) {
		WARN_ON(1);
		goto out;
94 95
	}

A
Al Viro 已提交
96
	table = table ? table->child : head->ctl_table;
97

A
Al Viro 已提交
98 99 100 101 102 103 104 105 106
	p = find_in_table(table, name);
	if (!p) {
		for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) {
			if (h->attached_to != table)
				continue;
			p = find_in_table(h->attached_by, name);
			if (p)
				break;
		}
107 108
	}

A
Al Viro 已提交
109
	if (!p)
110 111 112
		goto out;

	err = ERR_PTR(-ENOMEM);
A
Al Viro 已提交
113 114 115 116
	inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
	if (h)
		sysctl_head_finish(h);

117 118 119 120 121 122 123 124 125 126 127 128
	if (!inode)
		goto out;

	err = NULL;
	dentry->d_op = &proc_sys_dentry_operations;
	d_add(dentry, inode);

out:
	sysctl_head_finish(head);
	return err;
}

129 130
static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf,
		size_t count, loff_t *ppos, int write)
131
{
A
Al Viro 已提交
132 133 134
	struct inode *inode = filp->f_path.dentry->d_inode;
	struct ctl_table_header *head = grab_header(inode);
	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
135 136
	ssize_t error;
	size_t res;
137

A
Al Viro 已提交
138 139
	if (IS_ERR(head))
		return PTR_ERR(head);
140 141 142 143 144 145

	/*
	 * At this point we know that the sysctl was not unregistered
	 * and won't be until we finish.
	 */
	error = -EPERM;
146
	if (sysctl_perm(head->root, table, write ? MAY_WRITE : MAY_READ))
147 148
		goto out;

A
Al Viro 已提交
149 150 151 152 153
	/* if that can happen at all, it should be -EINVAL, not -EISDIR */
	error = -EINVAL;
	if (!table->proc_handler)
		goto out;

154 155
	/* careful: calling conventions are nasty here */
	res = count;
156
	error = table->proc_handler(table, write, filp, buf, &res, ppos);
157 158 159 160 161 162 163 164
	if (!error)
		error = res;
out:
	sysctl_head_finish(head);

	return error;
}

165
static ssize_t proc_sys_read(struct file *filp, char __user *buf,
166 167
				size_t count, loff_t *ppos)
{
168 169
	return proc_sys_call_handler(filp, (void __user *)buf, count, ppos, 0);
}
170

171 172 173 174
static ssize_t proc_sys_write(struct file *filp, const char __user *buf,
				size_t count, loff_t *ppos)
{
	return proc_sys_call_handler(filp, (void __user *)buf, count, ppos, 1);
175 176 177 178
}


static int proc_sys_fill_cache(struct file *filp, void *dirent,
A
Al Viro 已提交
179 180 181
				filldir_t filldir,
				struct ctl_table_header *head,
				struct ctl_table *table)
182 183 184 185 186 187 188 189 190 191 192 193 194
{
	struct dentry *child, *dir = filp->f_path.dentry;
	struct inode *inode;
	struct qstr qname;
	ino_t ino = 0;
	unsigned type = DT_UNKNOWN;

	qname.name = table->procname;
	qname.len  = strlen(table->procname);
	qname.hash = full_name_hash(qname.name, qname.len);

	child = d_lookup(dir, &qname);
	if (!child) {
A
Al Viro 已提交
195 196 197 198 199 200 201 202 203
		child = d_alloc(dir, &qname);
		if (child) {
			inode = proc_sys_make_inode(dir->d_sb, head, table);
			if (!inode) {
				dput(child);
				return -ENOMEM;
			} else {
				child->d_op = &proc_sys_dentry_operations;
				d_add(child, inode);
204
			}
A
Al Viro 已提交
205 206
		} else {
			return -ENOMEM;
207 208 209
		}
	}
	inode = child->d_inode;
A
Al Viro 已提交
210 211
	ino  = inode->i_ino;
	type = inode->i_mode >> 12;
212
	dput(child);
A
Al Viro 已提交
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
}

static int scan(struct ctl_table_header *head, ctl_table *table,
		unsigned long *pos, struct file *file,
		void *dirent, filldir_t filldir)
{

	for (; table->ctl_name || table->procname; table++, (*pos)++) {
		int res;

		/* Can't do anything without a proc name */
		if (!table->procname)
			continue;

		if (*pos < file->f_pos)
			continue;

		res = proc_sys_fill_cache(file, dirent, filldir, head, table);
		if (res)
			return res;

		file->f_pos = *pos + 1;
	}
	return 0;
238 239 240 241
}

static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
A
Al Viro 已提交
242
	struct dentry *dentry = filp->f_path.dentry;
243
	struct inode *inode = dentry->d_inode;
A
Al Viro 已提交
244 245 246
	struct ctl_table_header *head = grab_header(inode);
	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
	struct ctl_table_header *h = NULL;
247
	unsigned long pos;
A
Al Viro 已提交
248 249 250 251
	int ret = -EINVAL;

	if (IS_ERR(head))
		return PTR_ERR(head);
252

A
Al Viro 已提交
253 254
	if (table && !table->child) {
		WARN_ON(1);
255
		goto out;
A
Al Viro 已提交
256 257 258
	}

	table = table ? table->child : head->ctl_table;
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275

	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;

A
Al Viro 已提交
276 277 278
	ret = scan(head, table, &pos, filp, dirent, filldir);
	if (ret)
		goto out;
279

A
Al Viro 已提交
280 281
	for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) {
		if (h->attached_to != table)
282
			continue;
A
Al Viro 已提交
283 284 285 286
		ret = scan(h, h->attached_by, &pos, filp, dirent, filldir);
		if (ret) {
			sysctl_head_finish(h);
			break;
287 288 289 290 291 292 293 294
		}
	}
	ret = 1;
out:
	sysctl_head_finish(head);
	return ret;
}

295
static int proc_sys_permission(struct inode *inode, int mask)
296 297 298 299 300
{
	/*
	 * sysctl entries that are not writeable,
	 * are _NOT_ writeable, capabilities or not.
	 */
301 302
	struct ctl_table_header *head;
	struct ctl_table *table;
303 304
	int error;

305 306 307 308 309
	/* Executable files are not allowed under /proc/sys/ */
	if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))
		return -EACCES;

	head = grab_header(inode);
A
Al Viro 已提交
310 311
	if (IS_ERR(head))
		return PTR_ERR(head);
312

313
	table = PROC_I(inode)->sysctl_entry;
A
Al Viro 已提交
314 315 316 317
	if (!table) /* global root - r-xr-xr-x */
		error = mask & MAY_WRITE ? -EACCES : 0;
	else /* Use the permissions on the sysctl table entry */
		error = sysctl_perm(head->root, table, mask);
318 319 320 321 322 323 324 325 326 327 328 329 330 331

	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);
332 333
	if (!error)
		error = inode_setattr(inode, attr);
334 335 336 337

	return error;
}

A
Al Viro 已提交
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{
	struct inode *inode = dentry->d_inode;
	struct ctl_table_header *head = grab_header(inode);
	struct ctl_table *table = PROC_I(inode)->sysctl_entry;

	if (IS_ERR(head))
		return PTR_ERR(head);

	generic_fillattr(inode, stat);
	if (table)
		stat->mode = (stat->mode & S_IFMT) | table->mode;

	sysctl_head_finish(head);
	return 0;
}

355 356 357
static const struct file_operations proc_sys_file_operations = {
	.read		= proc_sys_read,
	.write		= proc_sys_write,
A
Al Viro 已提交
358 359 360
};

static const struct file_operations proc_sys_dir_file_operations = {
361
	.readdir	= proc_sys_readdir,
362
	.llseek		= generic_file_llseek,
363 364
};

365
static const struct inode_operations proc_sys_inode_operations = {
A
Al Viro 已提交
366 367 368 369 370 371
	.permission	= proc_sys_permission,
	.setattr	= proc_sys_setattr,
	.getattr	= proc_sys_getattr,
};

static const struct inode_operations proc_sys_dir_operations = {
372 373 374
	.lookup		= proc_sys_lookup,
	.permission	= proc_sys_permission,
	.setattr	= proc_sys_setattr,
A
Al Viro 已提交
375
	.getattr	= proc_sys_getattr,
376 377 378 379
};

static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd)
{
A
Al Viro 已提交
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
	return !PROC_I(dentry->d_inode)->sysctl->unregistering;
}

static int proc_sys_delete(struct dentry *dentry)
{
	return !!PROC_I(dentry->d_inode)->sysctl->unregistering;
}

static int proc_sys_compare(struct dentry *dir, struct qstr *qstr,
			    struct qstr *name)
{
	struct dentry *dentry = container_of(qstr, struct dentry, d_name);
	if (qstr->len != name->len)
		return 1;
	if (memcmp(qstr->name, name->name, name->len))
		return 1;
	return !sysctl_is_seen(PROC_I(dentry->d_inode)->sysctl);
397 398
}

A
Al Viro 已提交
399
static const struct dentry_operations proc_sys_dentry_operations = {
400
	.d_revalidate	= proc_sys_revalidate,
A
Al Viro 已提交
401 402
	.d_delete	= proc_sys_delete,
	.d_compare	= proc_sys_compare,
403 404
};

A
Alexey Dobriyan 已提交
405
int __init proc_sys_init(void)
406
{
A
Alexey Dobriyan 已提交
407 408
	struct proc_dir_entry *proc_sys_root;

409
	proc_sys_root = proc_mkdir("sys", NULL);
A
Al Viro 已提交
410 411
	proc_sys_root->proc_iops = &proc_sys_dir_operations;
	proc_sys_root->proc_fops = &proc_sys_dir_file_operations;
412 413 414
	proc_sys_root->nlink = 0;
	return 0;
}