This source file includes following definitions.
- sysctl_init
- do_sysctl
- sys_sysctl
- in_egroup_p
- test_perm
- ctl_perm
- parse_table
- do_sysctl_strategy
- do_securelevel_strategy
- register_sysctl_table
- unregister_sysctl_table
- register_proc_table
- unregister_proc_table
- do_rw_proc
- proc_readsys
- proc_writesys
- proc_sys_permission
- proc_dostring
- proc_dointvec
- proc_dointvec_minmax
- proc_dostring
- proc_dointvec
- proc_dointvec_minmax
- sysctl_string
- sysctl_intvec
- do_string
- do_int
- do_struct
1
2
3
4
5
6
7
8
9 #include <linux/config.h>
10 #include <linux/sched.h>
11 #include <linux/mm.h>
12 #include <linux/sysctl.h>
13 #include <linux/swapctl.h>
14 #include <linux/proc_fs.h>
15 #include <linux/malloc.h>
16 #include <linux/stat.h>
17 #include <linux/ctype.h>
18 #include <asm/bitops.h>
19 #include <asm/segment.h>
20
21 #include <linux/utsname.h>
22 #include <linux/swapctl.h>
23
24
25 extern int panic_timeout;
26
27
28 static ctl_table root_table[];
29 static struct ctl_table_header root_table_header =
30 {root_table, DNODE_SINGLE(&root_table_header)};
31
32 static int parse_table(int *, int, void *, size_t *, void *, size_t,
33 ctl_table *, void **);
34
35 static ctl_table kern_table[];
36 static ctl_table vm_table[];
37
38
39
40 #ifdef CONFIG_PROC_FS
41
42 static int proc_readsys(struct inode * inode, struct file * file,
43 char * buf, int count);
44 static int proc_writesys(struct inode * inode, struct file * file,
45 const char * buf, int count);
46 static int proc_sys_permission(struct inode *, int);
47
48 struct file_operations proc_sys_file_operations =
49 {
50 NULL,
51 proc_readsys,
52 proc_writesys,
53 NULL,
54 NULL,
55 NULL,
56 NULL,
57 NULL,
58 NULL,
59 NULL
60 };
61
62 struct inode_operations proc_sys_inode_operations =
63 {
64 &proc_sys_file_operations,
65 NULL,
66 NULL,
67 NULL,
68 NULL,
69 NULL,
70 NULL,
71 NULL,
72 NULL,
73 NULL,
74 NULL,
75 NULL,
76 NULL,
77 NULL,
78 NULL,
79 NULL,
80 proc_sys_permission
81 };
82
83 extern struct proc_dir_entry proc_sys_root;
84
85 static void register_proc_table(ctl_table *, struct proc_dir_entry *);
86 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
87 #endif
88
89 extern int bdf_prm[], bdflush_min[], bdflush_max[];
90
91 static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
92 void *, size_t, void **);
93
94
95
96 static ctl_table root_table[] = {
97 {CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
98 {CTL_VM, "vm", NULL, 0, 0555, vm_table},
99 {0}
100 };
101
102 static ctl_table kern_table[] = {
103 {KERN_OSTYPE, "ostype", system_utsname.sysname, 64,
104 0444, NULL, &proc_dostring, &sysctl_string},
105 {KERN_OSRELEASE, "osrelease", system_utsname.release, 64,
106 0444, NULL, &proc_dostring, &sysctl_string},
107 {KERN_VERSION, "version", system_utsname.version, 64,
108 0444, NULL, &proc_dostring, &sysctl_string},
109 {KERN_NODENAME, "hostname", system_utsname.nodename, 64,
110 0644, NULL, &proc_dostring, &sysctl_string},
111 {KERN_DOMAINNAME, "domainname", system_utsname.domainname, 64,
112 0644, NULL, &proc_dostring, &sysctl_string},
113 {KERN_NRINODE, "inode-nr", &nr_inodes, 2*sizeof(int),
114 0444, NULL, &proc_dointvec},
115 {KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int),
116 0644, NULL, &proc_dointvec},
117 {KERN_NRFILE, "file-nr", &nr_files, sizeof(int),
118 0444, NULL, &proc_dointvec},
119 {KERN_MAXFILE, "file-max", &max_files, sizeof(int),
120 0644, NULL, &proc_dointvec},
121 {KERN_SECURELVL, "securelevel", &securelevel, sizeof(int),
122 0444, NULL, &proc_dointvec, (ctl_handler *)&do_securelevel_strategy},
123 {KERN_PANIC, "panic", &panic_timeout, sizeof(int),
124 0644, NULL, &proc_dointvec},
125 {0}
126 };
127
128 static ctl_table vm_table[] = {
129 {VM_SWAPCTL, "swapctl",
130 &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
131 {VM_KSWAPD, "kswapd",
132 &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
133 {VM_FREEPG, "freepages",
134 &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
135 {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
136 &proc_dointvec_minmax, &sysctl_intvec, NULL,
137 &bdflush_min, &bdflush_max},
138 {0}
139 };
140
141 void sysctl_init(void)
142 {
143 #ifdef CONFIG_PROC_FS
144 register_proc_table(root_table, &proc_sys_root);
145 #endif
146 }
147
148
149 int do_sysctl (int *name, int nlen,
150 void *oldval, size_t *oldlenp,
151 void *newval, size_t newlen)
152 {
153 int error;
154 struct ctl_table_header *tmp;
155 void *context;
156
157 if (nlen == 0 || nlen >= CTL_MAXNAME)
158 return -ENOTDIR;
159
160 error = verify_area(VERIFY_READ,name,nlen*sizeof(int));
161 if (error) return error;
162 if (oldval) {
163 if (!oldlenp)
164 return -EFAULT;
165 error = verify_area(VERIFY_WRITE,oldlenp,sizeof(size_t));
166 if (error) return error;
167 error = verify_area(VERIFY_WRITE,oldval,get_user(oldlenp));
168 if (error) return error;
169 }
170 if (newval) {
171 error = verify_area(VERIFY_READ,newval,newlen);
172 if (error) return error;
173 }
174 tmp = &root_table_header;
175 do {
176 context = 0;
177 error = parse_table(name, nlen, oldval, oldlenp,
178 newval, newlen, root_table, &context);
179 if (context)
180 kfree(context);
181 if (error != ENOTDIR)
182 return error;
183 tmp = tmp->DLIST_NEXT(ctl_entry);
184 } while (tmp != &root_table_header);
185 return -ENOTDIR;
186 }
187
188 extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
189 {
190 struct __sysctl_args tmp;
191 int error;
192 error = verify_area(VERIFY_READ, args, sizeof(*args));
193 if (error)
194 return error;
195 memcpy_fromfs(&tmp, args, sizeof(tmp));
196 return do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
197 tmp.newval, tmp.newlen);
198 }
199
200
201 static int in_egroup_p(gid_t grp)
202 {
203 int i;
204
205 if (grp == current->euid)
206 return 1;
207
208 for (i = 0; i < NGROUPS; i++) {
209 if (current->groups[i] == NOGROUP)
210 break;
211 if (current->groups[i] == grp)
212 return 1;
213 }
214 return 0;
215 }
216
217
218 static int test_perm(int mode, int op)
219 {
220 if (!current->euid)
221 mode >>= 6;
222 else if (in_egroup_p(0))
223 mode >>= 3;
224 if ((mode & op & 0007) == op)
225 return 0;
226 return -EACCES;
227 }
228 static inline int ctl_perm(ctl_table *table, int op)
229 {
230 return test_perm(table->mode, op);
231 }
232
233 static int parse_table(int *name, int nlen,
234 void *oldval, size_t *oldlenp,
235 void *newval, size_t newlen,
236 ctl_table *table, void **context)
237 {
238 int error;
239 repeat:
240 if (!nlen)
241 return -ENOTDIR;
242
243 for ( ; table->ctl_name; table++) {
244 if (get_user(name) == table->ctl_name ||
245 table->ctl_name == CTL_ANY) {
246 if (table->child) {
247 if (ctl_perm(table, 001))
248 return -EPERM;
249 if (table->strategy) {
250 error = table->strategy(
251 table, name, nlen,
252 oldval, oldlenp,
253 newval, newlen, context);
254 if (error)
255 return error;
256 }
257 name++;
258 nlen--;
259 table = table->child;
260 goto repeat;
261 }
262 error = do_sysctl_strategy(table, name, nlen,
263 oldval, oldlenp,
264 newval, newlen, context);
265 return error;
266 }
267 };
268 return -ENOTDIR;
269 }
270
271
272 int do_sysctl_strategy (ctl_table *table,
273 int *name, int nlen,
274 void *oldval, size_t *oldlenp,
275 void *newval, size_t newlen, void **context)
276 {
277 int op = 0, rc, len;
278
279 if (oldval)
280 op |= 004;
281 if (newval)
282 op |= 002;
283 if (ctl_perm(table, op))
284 return -EPERM;
285
286 if (table->strategy) {
287 rc = table->strategy(table, name, nlen, oldval, oldlenp,
288 newval, newlen, context);
289 if (rc < 0)
290 return rc;
291 if (rc > 0)
292 return 0;
293 }
294
295
296
297 if (table->data && table->maxlen) {
298 if (oldval && oldlenp && get_user(oldlenp)) {
299 len = get_user(oldlenp);
300 if (len > table->maxlen)
301 len = table->maxlen;
302 memcpy_tofs(oldval, table->data, len);
303 put_user(len, oldlenp);
304 }
305 if (newval && newlen) {
306 len = newlen;
307 if (len > table->maxlen)
308 len = table->maxlen;
309 memcpy_fromfs(table->data, newval, len);
310 }
311 }
312 return 0;
313 }
314
315
316
317
318
319
320 static int do_securelevel_strategy (ctl_table *table,
321 int *name, int nlen,
322 void *oldval, size_t *oldlenp,
323 void *newval, size_t newlen, void **context)
324 {
325 int level;
326
327 if (newval && newlen) {
328 if (newlen != sizeof (int))
329 return -EINVAL;
330 memcpy_fromfs (&level, newval, newlen);
331 if (level < securelevel && current->pid != 1)
332 return -EPERM;
333 }
334 return 0;
335 }
336
337 struct ctl_table_header *register_sysctl_table(ctl_table * table,
338 int insert_at_head)
339 {
340 struct ctl_table_header *tmp;
341 tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
342 if (!tmp)
343 return 0;
344 *tmp = ((struct ctl_table_header) {table, DNODE_NULL});
345 if (insert_at_head)
346 DLIST_INSERT_AFTER(&root_table_header, tmp, ctl_entry);
347 else
348 DLIST_INSERT_BEFORE(&root_table_header, tmp, ctl_entry);
349 #ifdef CONFIG_PROC_FS
350 register_proc_table(table, &proc_sys_root);
351 #endif
352 return tmp;
353 }
354
355 void unregister_sysctl_table(struct ctl_table_header * table)
356 {
357 DLIST_DELETE(table, ctl_entry);
358 #ifdef CONFIG_PROC_FS
359 unregister_proc_table(table->ctl_table, &proc_sys_root);
360 #endif
361 }
362
363
364
365
366
367 #ifdef CONFIG_PROC_FS
368
369
370 static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
371 {
372 struct proc_dir_entry *de;
373
374 for (; table->ctl_name; table++) {
375
376 if (!table->procname)
377 continue;
378
379 if (!table->proc_handler &&
380 !table->child)
381 continue;
382
383 de = kmalloc(sizeof(*de), GFP_KERNEL);
384 if (!de) continue;
385 de->namelen = strlen(table->procname);
386 de->name = table->procname;
387 de->mode = table->mode;
388 de->nlink = 1;
389 de->uid = 0;
390 de->gid = 0;
391 de->size = 0;
392 de->get_info = 0;
393 de->fill_inode = 0;
394 de->next = de->subdir = 0;
395 de->data = (void *) table;
396
397 if (table->proc_handler) {
398 de->ops = &proc_sys_inode_operations;
399 de->mode |= S_IFREG;
400 }
401
402 else {
403 de->ops = &proc_dir_inode_operations;
404 de->nlink++;
405 de->mode |= S_IFDIR;
406 }
407 table->de = de;
408 proc_register_dynamic(root, de);
409 if (de->mode & S_IFDIR )
410 register_proc_table(table->child, de);
411 }
412 }
413
414 static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
415 {
416 struct proc_dir_entry *de;
417 for (; table->ctl_name; table++) {
418 if (!(de = table->de))
419 continue;
420 if (de->mode & S_IFDIR) {
421 if (!table->child) {
422 printk (KERN_ALERT "Help - malformed sysctl tree on free\n");
423 continue;
424 }
425 unregister_proc_table(table->child, de);
426 }
427 proc_unregister(root, de->low_ino);
428 kfree(de);
429 }
430 }
431
432
433 static int do_rw_proc(int write, struct inode * inode, struct file * file,
434 char * buf, int count)
435 {
436 int error, op;
437 struct proc_dir_entry *de;
438 struct ctl_table *table;
439 size_t res;
440
441 error = verify_area(write ? VERIFY_READ : VERIFY_WRITE, buf, count);
442 if (error)
443 return error;
444
445 de = (struct proc_dir_entry*) inode->u.generic_ip;
446 if (!de || !de->data)
447 return -ENOTDIR;
448 table = (struct ctl_table *) de->data;
449 if (!table || !table->proc_handler)
450 return -ENOTDIR;
451 op = (write ? 002 : 004);
452 if (ctl_perm(table, op))
453 return -EPERM;
454
455 res = count;
456 error = (*table->proc_handler) (table, write, file, buf, &res);
457 if (error)
458 return error;
459 return res;
460 }
461
462 static int proc_readsys(struct inode * inode, struct file * file,
463 char * buf, int count)
464 {
465 return do_rw_proc(0, inode, file, buf, count);
466 }
467
468 static int proc_writesys(struct inode * inode, struct file * file,
469 const char * buf, int count)
470 {
471 return do_rw_proc(1, inode, file, (char *) buf, count);
472 }
473
474 static int proc_sys_permission(struct inode *inode, int op)
475 {
476 return test_perm(inode->i_mode, op);
477 }
478
479 int proc_dostring(ctl_table *table, int write, struct file *filp,
480 void *buffer, size_t *lenp)
481 {
482 int len;
483 char *p, c;
484
485 if (!table->data || !table->maxlen || !*lenp ||
486 (filp->f_pos && !write)) {
487 *lenp = 0;
488 return 0;
489 }
490
491 if (write) {
492 len = 0;
493 p = buffer;
494 while (len < *lenp &&
495 (c = get_user(p++)) != 0 && c != '\n')
496 len++;
497 if (len >= table->maxlen)
498 len = table->maxlen-1;
499 memcpy_fromfs(table->data, buffer, len);
500 ((char *) table->data)[len] = 0;
501 filp->f_pos += *lenp;
502 } else {
503 len = strlen(table->data) + 1;
504 if (len > table->maxlen)
505 len = table->maxlen;
506 if (len > *lenp)
507 len = *lenp;
508 if (len) {
509 memcpy_tofs(buffer, table->data, len-1);
510 put_user(0, ((char *) buffer) + len - 1);
511 }
512 if (len < *lenp) {
513 put_user('\n', ((char *) buffer) + len);
514 len++;
515 }
516 *lenp = len;
517 filp->f_pos += len;
518 }
519 return 0;
520 }
521
522 int proc_dointvec(ctl_table *table, int write, struct file *filp,
523 void *buffer, size_t *lenp)
524 {
525 int *i, vleft, first=1, len, left, neg, val;
526 #define TMPBUFLEN 20
527 char buf[TMPBUFLEN], *p;
528
529 if (!table->data || !table->maxlen || !*lenp ||
530 (filp->f_pos && !write)) {
531 *lenp = 0;
532 return 0;
533 }
534
535 i = (int *) table->data;
536 vleft = table->maxlen / sizeof(int);
537 left = *lenp;
538
539 for (; left && vleft--; i++, first=0) {
540 if (write) {
541 while (left && isspace(get_user((char *) buffer)))
542 left--, ((char *) buffer)++;
543 if (!left)
544 break;
545 neg = 0;
546 len = left;
547 if (len > TMPBUFLEN-1)
548 len = TMPBUFLEN-1;
549 memcpy_fromfs(buf, buffer, len);
550 buf[len] = 0;
551 p = buf;
552 if (*p == '-' && left > 1) {
553 neg = 1;
554 left--, p++;
555 }
556 if (*p < '0' || *p > '9')
557 break;
558 val = simple_strtoul(p, &p, 0);
559 len = p-buf;
560 if ((len < left) && *p && !isspace(*p))
561 break;
562 if (neg)
563 val = -val;
564 buffer += len;
565 left -= len;
566 *i = val;
567 } else {
568 p = buf;
569 if (!first)
570 *p++ = '\t';
571 sprintf(p, "%d", *i);
572 len = strlen(buf);
573 if (len > left)
574 len = left;
575 memcpy_tofs(buffer, buf, len);
576 left -= len;
577 buffer += len;
578 }
579 }
580
581 if (!write && !first && left) {
582 put_user('\n', (char *) buffer);
583 left--, buffer++;
584 }
585 if (write) {
586 p = (char *) buffer;
587 while (left && isspace(get_user(p++)))
588 left--;
589 }
590 if (write && first)
591 return -EINVAL;
592 *lenp -= left;
593 filp->f_pos += *lenp;
594 return 0;
595 }
596
597 int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
598 void *buffer, size_t *lenp)
599 {
600 int *i, *min, *max, vleft, first=1, len, left, neg, val;
601 #define TMPBUFLEN 20
602 char buf[TMPBUFLEN], *p;
603
604 if (!table->data || !table->maxlen || !*lenp ||
605 (filp->f_pos && !write)) {
606 *lenp = 0;
607 return 0;
608 }
609
610 i = (int *) table->data;
611 min = (int *) table->extra1;
612 max = (int *) table->extra2;
613 vleft = table->maxlen / sizeof(int);
614 left = *lenp;
615
616 for (; left && vleft--; i++, first=0) {
617 if (write) {
618 while (left && isspace(get_user((char *) buffer)))
619 left--, ((char *) buffer)++;
620 if (!left)
621 break;
622 neg = 0;
623 len = left;
624 if (len > TMPBUFLEN-1)
625 len = TMPBUFLEN-1;
626 memcpy_fromfs(buf, buffer, len);
627 buf[len] = 0;
628 p = buf;
629 if (*p == '-' && left > 1) {
630 neg = 1;
631 left--, p++;
632 }
633 if (*p < '0' || *p > '9')
634 break;
635 val = simple_strtoul(p, &p, 0);
636 len = p-buf;
637 if ((len < left) && *p && !isspace(*p))
638 break;
639 if (neg)
640 val = -val;
641 buffer += len;
642 left -= len;
643
644 if (min && val < *min++)
645 continue;
646 if (max && val > *max++)
647 continue;
648 *i = val;
649 } else {
650 p = buf;
651 if (!first)
652 *p++ = '\t';
653 sprintf(p, "%d", *i);
654 len = strlen(buf);
655 if (len > left)
656 len = left;
657 memcpy_tofs(buffer, buf, len);
658 left -= len;
659 buffer += len;
660 }
661 }
662
663 if (!write && !first && left) {
664 put_user('\n', (char *) buffer);
665 left--, buffer++;
666 }
667 if (write) {
668 p = (char *) buffer;
669 while (left && isspace(get_user(p++)))
670 left--;
671 }
672 if (write && first)
673 return -EINVAL;
674 *lenp -= left;
675 filp->f_pos += *lenp;
676 return 0;
677 }
678
679 #else
680
681 int proc_dostring(ctl_table *table, int write, struct file *filp,
682 void *buffer, size_t *lenp)
683 {
684 return -ENOSYS;
685 }
686
687 int proc_dointvec(ctl_table *table, int write, struct file *filp,
688 void *buffer, size_t *lenp)
689 {
690 return -ENOSYS;
691 }
692
693 int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
694 void *buffer, size_t *lenp)
695 {
696 return -ENOSYS;
697 }
698
699 #endif
700
701
702
703
704
705
706
707 int sysctl_string(ctl_table *table, int *name, int nlen,
708 void *oldval, size_t *oldlenp,
709 void *newval, size_t newlen, void **context)
710 {
711 int l, len;
712
713 if (!table->data || !table->maxlen)
714 return -ENOTDIR;
715
716 if (oldval && oldlenp && get_user(oldlenp)) {
717 len = get_user(oldlenp);
718 l = strlen(table->data);
719 if (len > l) len = l;
720 if (len >= table->maxlen)
721 len = table->maxlen;
722 memcpy_tofs(oldval, table->data, len);
723 put_user(0, ((char *) oldval) + len);
724 put_user(len, oldlenp);
725 }
726 if (newval && newlen) {
727 len = newlen;
728 if (len > table->maxlen)
729 len = table->maxlen;
730 memcpy_fromfs(table->data, newval, len);
731 if (len == table->maxlen)
732 len--;
733 ((char *) table->data)[len] = 0;
734 }
735 return 0;
736 }
737
738
739
740
741
742
743 int sysctl_intvec(ctl_table *table, int *name, int nlen,
744 void *oldval, size_t *oldlenp,
745 void *newval, size_t newlen, void **context)
746 {
747 int i, length, *vec, *min, *max;
748
749 if (newval && newlen) {
750 if (newlen % sizeof(int) != 0)
751 return -EINVAL;
752
753 if (!table->extra1 && !table->extra2)
754 return 0;
755
756 if (newlen > table->maxlen)
757 newlen = table->maxlen;
758 length = newlen / sizeof(int);
759
760 vec = (int *) newval;
761 min = (int *) table->extra1;
762 max = (int *) table->extra2;
763
764 for (i = 0; i < length; i++) {
765 int value = get_user(vec + i);
766 if (min && value < min[i])
767 return -EINVAL;
768 if (max && value > max[i])
769 return -EINVAL;
770 }
771 }
772 return 0;
773 }
774
775 int do_string (
776 void *oldval, size_t *oldlenp, void *newval, size_t newlen,
777 int rdwr, char *data, size_t max)
778 {
779 int l = strlen(data) + 1;
780 if (newval && !rdwr)
781 return -EPERM;
782 if (newval && newlen >= max)
783 return -EINVAL;
784 if (oldval) {
785 if (l > get_user(oldlenp))
786 return -ENOMEM;
787 put_user(l, oldlenp);
788 memcpy_tofs(oldval, data, l);
789 }
790 if (newval) {
791 memcpy_fromfs(data, newval, newlen);
792 data[newlen] = 0;
793 }
794 return 0;
795 }
796
797 int do_int (
798 void *oldval, size_t *oldlenp, void *newval, size_t newlen,
799 int rdwr, int *data)
800 {
801 if (newval && !rdwr)
802 return -EPERM;
803 if (newval && newlen != sizeof(int))
804 return -EINVAL;
805 if (oldval) {
806 if (get_user(oldlenp) < sizeof(int))
807 return -ENOMEM;
808 put_user(sizeof(int), oldlenp);
809 memcpy_tofs(oldval, data, sizeof(int));
810 }
811 if (newval)
812 memcpy_fromfs(data, newval, sizeof(int));
813 return 0;
814 }
815
816 int do_struct (
817 void *oldval, size_t *oldlenp, void *newval, size_t newlen,
818 int rdwr, void *data, size_t len)
819 {
820 if (newval && !rdwr)
821 return -EPERM;
822 if (newval && newlen != len)
823 return -EINVAL;
824 if (oldval) {
825 if (get_user(oldlenp) < len)
826 return -ENOMEM;
827 put_user(len, oldlenp);
828 memcpy_tofs(oldval, data, len);
829 }
830 if (newval)
831 memcpy_fromfs(data, newval, len);
832 return 0;
833 }
834