Merge branch 'master' into for-linus
[pandora-kernel.git] / kernel / sysctl_check.c
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 }