]>
Commit | Line | Data |
---|---|---|
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 | ||
9 | static 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 | ||
21 | static 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 | ||
32 | static 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 | ||
46 | static 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; | |
59 | repeat: | |
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; | |
81 | out: | |
82 | sysctl_head_finish(head); | |
83 | return ref; | |
84 | } | |
85 | ||
86 | static 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 | ||
97 | static 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 | ||
107 | int 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 | #if 0 | |
151 | if (!table->procname && table->proc_handler) | |
152 | set_fail(&fail, table, "proc_handler without procname"); | |
153 | #endif | |
154 | sysctl_check_leaf(namespaces, table, &fail); | |
155 | } | |
156 | if (table->mode > 0777) | |
157 | set_fail(&fail, table, "bogus .mode"); | |
158 | if (fail) { | |
159 | set_fail(&fail, table, NULL); | |
160 | error = -EINVAL; | |
161 | } | |
162 | if (table->child) | |
163 | error |= sysctl_check_table(namespaces, table->child); | |
164 | } | |
165 | return error; | |
166 | } |