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