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