Fix pointer mismatches in proc_sysctl.c
[linux-2.6.git] / fs / proc / proc_sysctl.c
1 /*
2  * /proc/sys support
3  */
4
5 #include <linux/sysctl.h>
6 #include <linux/proc_fs.h>
7 #include <linux/security.h>
8 #include "internal.h"
9
10 static struct dentry_operations proc_sys_dentry_operations;
11 static const struct file_operations proc_sys_file_operations;
12 static struct inode_operations proc_sys_inode_operations;
13
14 static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table)
15 {
16         /* Refresh the cached information bits in the inode */
17         if (table) {
18                 inode->i_uid = 0;
19                 inode->i_gid = 0;
20                 inode->i_mode = table->mode;
21                 if (table->proc_handler) {
22                         inode->i_mode |= S_IFREG;
23                         inode->i_nlink = 1;
24                 } else {
25                         inode->i_mode |= S_IFDIR;
26                         inode->i_nlink = 0;     /* It is too hard to figure out */
27                 }
28         }
29 }
30
31 static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table)
32 {
33         struct inode *inode;
34         struct proc_inode *dir_ei, *ei;
35         int depth;
36
37         inode = new_inode(dir->i_sb);
38         if (!inode)
39                 goto out;
40
41         /* A directory is always one deeper than it's parent */
42         dir_ei = PROC_I(dir);
43         depth = dir_ei->fd + 1;
44
45         ei = PROC_I(inode);
46         ei->fd = depth;
47         inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
48         inode->i_op = &proc_sys_inode_operations;
49         inode->i_fop = &proc_sys_file_operations;
50         inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */
51         proc_sys_refresh_inode(inode, table);
52 out:
53         return inode;
54 }
55
56 static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth)
57 {
58         for (;;) {
59                 struct proc_inode *ei;
60
61                 ei = PROC_I(dentry->d_inode);
62                 if (ei->fd == depth)
63                         break; /* found */
64
65                 dentry = dentry->d_parent;
66         }
67         return dentry;
68 }
69
70 static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table,
71                                                         struct qstr *name)
72 {
73         int len;
74         for ( ; table->ctl_name || table->procname; table++) {
75
76                 if (!table->procname)
77                         continue;
78
79                 len = strlen(table->procname);
80                 if (len != name->len)
81                         continue;
82
83                 if (memcmp(table->procname, name->name, len) != 0)
84                         continue;
85
86                 /* I have a match */
87                 return table;
88         }
89         return NULL;
90 }
91
92 static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry,
93                                                 struct ctl_table *table)
94 {
95         struct dentry *ancestor;
96         struct proc_inode *ei;
97         int depth, i;
98
99         ei = PROC_I(dentry->d_inode);
100         depth = ei->fd;
101
102         if (depth == 0)
103                 return table;
104
105         for (i = 1; table && (i <= depth); i++) {
106                 ancestor = proc_sys_ancestor(dentry, i);
107                 table = proc_sys_lookup_table_one(table, &ancestor->d_name);
108                 if (table)
109                         table = table->child;
110         }
111         return table;
112
113 }
114 static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent,
115                                                 struct qstr *name,
116                                                 struct ctl_table *table)
117 {
118         table = proc_sys_lookup_table(dparent, table);
119         if (table)
120                 table = proc_sys_lookup_table_one(table, name);
121         return table;
122 }
123
124 static struct ctl_table *do_proc_sys_lookup(struct dentry *parent,
125                                                 struct qstr *name,
126                                                 struct ctl_table_header **ptr)
127 {
128         struct ctl_table_header *head;
129         struct ctl_table *table = NULL;
130
131         for (head = sysctl_head_next(NULL); head;
132                         head = sysctl_head_next(head)) {
133                 table = proc_sys_lookup_entry(parent, name, head->ctl_table);
134                 if (table)
135                         break;
136         }
137         *ptr = head;
138         return table;
139 }
140
141 static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
142                                         struct nameidata *nd)
143 {
144         struct ctl_table_header *head;
145         struct inode *inode;
146         struct dentry *err;
147         struct ctl_table *table;
148
149         err = ERR_PTR(-ENOENT);
150         table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
151         if (!table)
152                 goto out;
153
154         err = ERR_PTR(-ENOMEM);
155         inode = proc_sys_make_inode(dir, table);
156         if (!inode)
157                 goto out;
158
159         err = NULL;
160         dentry->d_op = &proc_sys_dentry_operations;
161         d_add(dentry, inode);
162
163 out:
164         sysctl_head_finish(head);
165         return err;
166 }
167
168 static ssize_t proc_sys_read(struct file *filp, char __user *buf,
169                                 size_t count, loff_t *ppos)
170 {
171         struct dentry *dentry = filp->f_dentry;
172         struct ctl_table_header *head;
173         struct ctl_table *table;
174         ssize_t error;
175         size_t res;
176
177         table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
178         /* Has the sysctl entry disappeared on us? */
179         error = -ENOENT;
180         if (!table)
181                 goto out;
182
183         /* Has the sysctl entry been replaced by a directory? */
184         error = -EISDIR;
185         if (!table->proc_handler)
186                 goto out;
187
188         /*
189          * At this point we know that the sysctl was not unregistered
190          * and won't be until we finish.
191          */
192         error = -EPERM;
193         if (sysctl_perm(table, MAY_READ))
194                 goto out;
195
196         /* careful: calling conventions are nasty here */
197         res = count;
198         error = table->proc_handler(table, 0, filp, buf, &res, ppos);
199         if (!error)
200                 error = res;
201 out:
202         sysctl_head_finish(head);
203
204         return error;
205 }
206
207 static ssize_t proc_sys_write(struct file *filp, const char __user *buf,
208                                 size_t count, loff_t *ppos)
209 {
210         struct dentry *dentry = filp->f_dentry;
211         struct ctl_table_header *head;
212         struct ctl_table *table;
213         ssize_t error;
214         size_t res;
215
216         table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
217         /* Has the sysctl entry disappeared on us? */
218         error = -ENOENT;
219         if (!table)
220                 goto out;
221
222         /* Has the sysctl entry been replaced by a directory? */
223         error = -EISDIR;
224         if (!table->proc_handler)
225                 goto out;
226
227         /*
228          * At this point we know that the sysctl was not unregistered
229          * and won't be until we finish.
230          */
231         error = -EPERM;
232         if (sysctl_perm(table, MAY_WRITE))
233                 goto out;
234
235         /* careful: calling conventions are nasty here */
236         res = count;
237         error = table->proc_handler(table, 1, filp, (char __user *)buf,
238                                     &res, ppos);
239         if (!error)
240                 error = res;
241 out:
242         sysctl_head_finish(head);
243
244         return error;
245 }
246
247
248 static int proc_sys_fill_cache(struct file *filp, void *dirent,
249                                 filldir_t filldir, struct ctl_table *table)
250 {
251         struct ctl_table_header *head;
252         struct ctl_table *child_table = NULL;
253         struct dentry *child, *dir = filp->f_path.dentry;
254         struct inode *inode;
255         struct qstr qname;
256         ino_t ino = 0;
257         unsigned type = DT_UNKNOWN;
258         int ret;
259
260         qname.name = table->procname;
261         qname.len  = strlen(table->procname);
262         qname.hash = full_name_hash(qname.name, qname.len);
263
264         /* Suppress duplicates.
265          * Only fill a directory entry if it is the value that
266          * an ordinary lookup of that name returns.  Hide all
267          * others.
268          *
269          * If we ever cache this translation in the dcache
270          * I should do a dcache lookup first.  But for now
271          * it is just simpler not to.
272          */
273         ret = 0;
274         child_table = do_proc_sys_lookup(dir, &qname, &head);
275         sysctl_head_finish(head);
276         if (child_table != table)
277                 return 0;
278
279         child = d_lookup(dir, &qname);
280         if (!child) {
281                 struct dentry *new;
282                 new = d_alloc(dir, &qname);
283                 if (new) {
284                         inode = proc_sys_make_inode(dir->d_inode, table);
285                         if (!inode)
286                                 child = ERR_PTR(-ENOMEM);
287                         else {
288                                 new->d_op = &proc_sys_dentry_operations;
289                                 d_add(new, inode);
290                         }
291                         if (child)
292                                 dput(new);
293                         else
294                                 child = new;
295                 }
296         }
297         if (!child || IS_ERR(child) || !child->d_inode)
298                 goto end_instantiate;
299         inode = child->d_inode;
300         if (inode) {
301                 ino  = inode->i_ino;
302                 type = inode->i_mode >> 12;
303         }
304         dput(child);
305 end_instantiate:
306         if (!ino)
307                 ino= find_inode_number(dir, &qname);
308         if (!ino)
309                 ino = 1;
310         return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
311 }
312
313 static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
314 {
315         struct dentry *dentry = filp->f_dentry;
316         struct inode *inode = dentry->d_inode;
317         struct ctl_table_header *head = NULL;
318         struct ctl_table *table;
319         unsigned long pos;
320         int ret;
321
322         ret = -ENOTDIR;
323         if (!S_ISDIR(inode->i_mode))
324                 goto out;
325
326         ret = 0;
327         /* Avoid a switch here: arm builds fail with missing __cmpdi2 */
328         if (filp->f_pos == 0) {
329                 if (filldir(dirent, ".", 1, filp->f_pos,
330                                 inode->i_ino, DT_DIR) < 0)
331                         goto out;
332                 filp->f_pos++;
333         }
334         if (filp->f_pos == 1) {
335                 if (filldir(dirent, "..", 2, filp->f_pos,
336                                 parent_ino(dentry), DT_DIR) < 0)
337                         goto out;
338                 filp->f_pos++;
339         }
340         pos = 2;
341
342         /* - Find each instance of the directory
343          * - Read all entries in each instance
344          * - Before returning an entry to user space lookup the entry
345          *   by name and if I find a different entry don't return
346          *   this one because it means it is a buried dup.
347          * For sysctl this should only happen for directory entries.
348          */
349         for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {
350                 table = proc_sys_lookup_table(dentry, head->ctl_table);
351
352                 if (!table)
353                         continue;
354
355                 for (; table->ctl_name || table->procname; table++, pos++) {
356                         /* Can't do anything without a proc name */
357                         if (!table->procname)
358                                 continue;
359
360                         if (pos < filp->f_pos)
361                                 continue;
362
363                         if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0)
364                                 goto out;
365                         filp->f_pos = pos + 1;
366                 }
367         }
368         ret = 1;
369 out:
370         sysctl_head_finish(head);
371         return ret;
372 }
373
374 static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd)
375 {
376         /*
377          * sysctl entries that are not writeable,
378          * are _NOT_ writeable, capabilities or not.
379          */
380         struct ctl_table_header *head;
381         struct ctl_table *table;
382         struct dentry *dentry;
383         int mode;
384         int depth;
385         int error;
386
387         head = NULL;
388         depth = PROC_I(inode)->fd;
389
390         /* First check the cached permissions, in case we don't have
391          * enough information to lookup the sysctl table entry.
392          */
393         error = -EACCES;
394         mode = inode->i_mode;
395
396         if (current->euid == 0)
397                 mode >>= 6;
398         else if (in_group_p(0))
399                 mode >>= 3;
400
401         if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)
402                 error = 0;
403
404         /* If we can't get a sysctl table entry the permission
405          * checks on the cached mode will have to be enough.
406          */
407         if (!nd || !depth)
408                 goto out;
409
410         dentry = nd->dentry;
411         table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
412
413         /* If the entry does not exist deny permission */
414         error = -EACCES;
415         if (!table)
416                 goto out;
417
418         /* Use the permissions on the sysctl table entry */
419         error = sysctl_perm(table, mask);
420 out:
421         sysctl_head_finish(head);
422         return error;
423 }
424
425 static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
426 {
427         struct inode *inode = dentry->d_inode;
428         int error;
429
430         if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
431                 return -EPERM;
432
433         error = inode_change_ok(inode, attr);
434         if (!error)
435                 error = inode_setattr(inode, attr);
436
437         return error;
438 }
439
440 /* I'm lazy and don't distinguish between files and directories,
441  * until access time.
442  */
443 static const struct file_operations proc_sys_file_operations = {
444         .read           = proc_sys_read,
445         .write          = proc_sys_write,
446         .readdir        = proc_sys_readdir,
447 };
448
449 static struct inode_operations proc_sys_inode_operations = {
450         .lookup         = proc_sys_lookup,
451         .permission     = proc_sys_permission,
452         .setattr        = proc_sys_setattr,
453 };
454
455 static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd)
456 {
457         struct ctl_table_header *head;
458         struct ctl_table *table;
459         table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
460         proc_sys_refresh_inode(dentry->d_inode, table);
461         sysctl_head_finish(head);
462         return !!table;
463 }
464
465 static struct dentry_operations proc_sys_dentry_operations = {
466         .d_revalidate   = proc_sys_revalidate,
467 };
468
469 static struct proc_dir_entry *proc_sys_root;
470
471 int proc_sys_init(void)
472 {
473         proc_sys_root = proc_mkdir("sys", NULL);
474         proc_sys_root->proc_iops = &proc_sys_inode_operations;
475         proc_sys_root->proc_fops = &proc_sys_file_operations;
476         proc_sys_root->nlink = 0;
477         return 0;
478 }