sysctl: Separate the binary sysctl logic into it's own file.
[safe/jmp/linux-2.6] / kernel / sysctl_binary.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 #include <linux/syscalls.h>
8 #include <linux/namei.h>
9 #include <linux/mount.h>
10 #include <linux/fs.h>
11 #include <linux/nsproxy.h>
12 #include <linux/pid_namespace.h>
13 #include <linux/file.h>
14 #include <linux/ctype.h>
15 #include <linux/smp_lock.h>
16
17 static int deprecated_sysctl_warning(struct __sysctl_args *args);
18
19 #ifdef CONFIG_SYSCTL_SYSCALL
20
21 /* Perform the actual read/write of a sysctl table entry. */
22 static int do_sysctl_strategy(struct ctl_table_root *root,
23                         struct ctl_table *table,
24                         void __user *oldval, size_t __user *oldlenp,
25                         void __user *newval, size_t newlen)
26 {
27         int op = 0, rc;
28
29         if (oldval)
30                 op |= MAY_READ;
31         if (newval)
32                 op |= MAY_WRITE;
33         if (sysctl_perm(root, table, op))
34                 return -EPERM;
35
36         if (table->strategy) {
37                 rc = table->strategy(table, oldval, oldlenp, newval, newlen);
38                 if (rc < 0)
39                         return rc;
40                 if (rc > 0)
41                         return 0;
42         }
43
44         /* If there is no strategy routine, or if the strategy returns
45          * zero, proceed with automatic r/w */
46         if (table->data && table->maxlen) {
47                 rc = sysctl_data(table, oldval, oldlenp, newval, newlen);
48                 if (rc < 0)
49                         return rc;
50         }
51         return 0;
52 }
53
54 static int parse_table(int __user *name, int nlen,
55                        void __user *oldval, size_t __user *oldlenp,
56                        void __user *newval, size_t newlen,
57                        struct ctl_table_root *root,
58                        struct ctl_table *table)
59 {
60         int n;
61 repeat:
62         if (!nlen)
63                 return -ENOTDIR;
64         if (get_user(n, name))
65                 return -EFAULT;
66         for ( ; table->ctl_name || table->procname; table++) {
67                 if (!table->ctl_name)
68                         continue;
69                 if (n == table->ctl_name) {
70                         int error;
71                         if (table->child) {
72                                 if (sysctl_perm(root, table, MAY_EXEC))
73                                         return -EPERM;
74                                 name++;
75                                 nlen--;
76                                 table = table->child;
77                                 goto repeat;
78                         }
79                         error = do_sysctl_strategy(root, table,
80                                                    oldval, oldlenp,
81                                                    newval, newlen);
82                         return error;
83                 }
84         }
85         return -ENOTDIR;
86 }
87
88 int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp,
89                void __user *newval, size_t newlen)
90 {
91         struct ctl_table_header *head;
92         int error = -ENOTDIR;
93
94         if (nlen <= 0 || nlen >= CTL_MAXNAME)
95                 return -ENOTDIR;
96         if (oldval) {
97                 int old_len;
98                 if (!oldlenp || get_user(old_len, oldlenp))
99                         return -EFAULT;
100         }
101
102         for (head = sysctl_head_next(NULL); head;
103                         head = sysctl_head_next(head)) {
104                 error = parse_table(name, nlen, oldval, oldlenp, 
105                                         newval, newlen,
106                                         head->root, head->ctl_table);
107                 if (error != -ENOTDIR) {
108                         sysctl_head_finish(head);
109                         break;
110                 }
111         }
112         return error;
113 }
114
115 SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
116 {
117         struct __sysctl_args tmp;
118         int error;
119
120         if (copy_from_user(&tmp, args, sizeof(tmp)))
121                 return -EFAULT;
122
123         error = deprecated_sysctl_warning(&tmp);
124         if (error)
125                 goto out;
126
127         lock_kernel();
128         error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
129                           tmp.newval, tmp.newlen);
130         unlock_kernel();
131 out:
132         return error;
133 }
134
135 #else /* CONFIG_SYSCTL_SYSCALL */
136
137 SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
138 {
139         struct __sysctl_args tmp;
140         int error;
141
142         if (copy_from_user(&tmp, args, sizeof(tmp)))
143                 return -EFAULT;
144
145         error = deprecated_sysctl_warning(&tmp);
146
147         /* If no error reading the parameters then just -ENOSYS ... */
148         if (!error)
149                 error = -ENOSYS;
150
151         return error;
152 }
153
154 #endif /* CONFIG_SYSCTL_SYSCALL */
155
156 static int deprecated_sysctl_warning(struct __sysctl_args *args)
157 {
158         static int msg_count;
159         int name[CTL_MAXNAME];
160         int i;
161
162         /* Check args->nlen. */
163         if (args->nlen < 0 || args->nlen > CTL_MAXNAME)
164                 return -ENOTDIR;
165
166         /* Read in the sysctl name for better debug message logging */
167         for (i = 0; i < args->nlen; i++)
168                 if (get_user(name[i], args->name + i))
169                         return -EFAULT;
170
171         /* Ignore accesses to kernel.version */
172         if ((args->nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION))
173                 return 0;
174
175         if (msg_count < 5) {
176                 msg_count++;
177                 printk(KERN_INFO
178                         "warning: process `%s' used the deprecated sysctl "
179                         "system call with ", current->comm);
180                 for (i = 0; i < args->nlen; i++)
181                         printk("%d.", name[i]);
182                 printk("\n");
183         }
184         return 0;
185 }