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