1#include <linux/stat.h>
2#include <linux/sysctl.h>
3#include "../fs/xfs/linux-2.6/xfs_sysctl.h"
4#include <linux/sunrpc/debug.h>
5#include <linux/string.h>
6#include <net/ip_vs.h>
7
8
9static int sysctl_depth(struct ctl_table *table)
10{
11	struct ctl_table *tmp;
12	int depth;
13
14	depth = 0;
15	for (tmp = table; tmp->parent; tmp = tmp->parent)
16		depth++;
17
18	return depth;
19}
20
21static struct ctl_table *sysctl_parent(struct ctl_table *table, int n)
22{
23	int i;
24
25	for (i = 0; table && i < n; i++)
26		table = table->parent;
27
28	return table;
29}
30
31
32static void sysctl_print_path(struct ctl_table *table)
33{
34	struct ctl_table *tmp;
35	int depth, i;
36	depth = sysctl_depth(table);
37	if (table->procname) {
38		for (i = depth; i >= 0; i--) {
39			tmp = sysctl_parent(table, i);
40			printk("/%s", tmp->procname?tmp->procname:"");
41		}
42	}
43	printk(" ");
44}
45
46static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces,
47						struct ctl_table *table)
48{
49	struct ctl_table_header *head;
50	struct ctl_table *ref, *test;
51	int depth, cur_depth;
52
53	depth = sysctl_depth(table);
54
55	for (head = __sysctl_head_next(namespaces, NULL); head;
56	     head = __sysctl_head_next(namespaces, head)) {
57		cur_depth = depth;
58		ref = head->ctl_table;
59repeat:
60		test = sysctl_parent(table, cur_depth);
61		for (; ref->procname; ref++) {
62			int match = 0;
63			if (cur_depth && !ref->child)
64				continue;
65
66			if (test->procname && ref->procname &&
67			    (strcmp(test->procname, ref->procname) == 0))
68					match++;
69
70			if (match) {
71				if (cur_depth != 0) {
72					cur_depth--;
73					ref = ref->child;
74					goto repeat;
75				}
76				goto out;
77			}
78		}
79	}
80	ref = NULL;
81out:
82	sysctl_head_finish(head);
83	return ref;
84}
85
86static void set_fail(const char **fail, struct ctl_table *table, const char *str)
87{
88	if (*fail) {
89		printk(KERN_ERR "sysctl table check failed: ");
90		sysctl_print_path(table);
91		printk(" %s\n", *fail);
92		dump_stack();
93	}
94	*fail = str;
95}
96
97static void sysctl_check_leaf(struct nsproxy *namespaces,
98				struct ctl_table *table, const char **fail)
99{
100	struct ctl_table *ref;
101
102	ref = sysctl_check_lookup(namespaces, table);
103	if (ref && (ref != table))
104		set_fail(fail, table, "Sysctl already exists");
105}
106
107int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table)
108{
109	int error = 0;
110	for (; table->procname; table++) {
111		const char *fail = NULL;
112
113		if (table->parent) {
114			if (table->procname && !table->parent->procname)
115				set_fail(&fail, table, "Parent without procname");
116		}
117		if (!table->procname)
118			set_fail(&fail, table, "No procname");
119		if (table->child) {
120			if (table->data)
121				set_fail(&fail, table, "Directory with data?");
122			if (table->maxlen)
123				set_fail(&fail, table, "Directory with maxlen?");
124			if ((table->mode & (S_IRUGO|S_IXUGO)) != table->mode)
125				set_fail(&fail, table, "Writable sysctl directory");
126			if (table->proc_handler)
127				set_fail(&fail, table, "Directory with proc_handler");
128			if (table->extra1)
129				set_fail(&fail, table, "Directory with extra1");
130			if (table->extra2)
131				set_fail(&fail, table, "Directory with extra2");
132		} else {
133			if ((table->proc_handler == proc_dostring) ||
134			    (table->proc_handler == proc_dointvec) ||
135			    (table->proc_handler == proc_dointvec_minmax) ||
136			    (table->proc_handler == proc_dointvec_jiffies) ||
137			    (table->proc_handler == proc_dointvec_userhz_jiffies) ||
138			    (table->proc_handler == proc_dointvec_ms_jiffies) ||
139			    (table->proc_handler == proc_doulongvec_minmax) ||
140			    (table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) {
141				if (!table->data)
142					set_fail(&fail, table, "No data");
143				if (!table->maxlen)
144					set_fail(&fail, table, "No maxlen");
145			}
146#ifdef CONFIG_PROC_SYSCTL
147			if (table->procname && !table->proc_handler)
148				set_fail(&fail, table, "No proc_handler");
149#endif
150			sysctl_check_leaf(namespaces, table, &fail);
151		}
152		if (table->mode > 0777)
153			set_fail(&fail, table, "bogus .mode");
154		if (fail) {
155			set_fail(&fail, table, NULL);
156			error = -EINVAL;
157		}
158		if (table->child)
159			error |= sysctl_check_table(namespaces, table->child);
160	}
161	return error;
162}
163