root/kernel/sysctl.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sysctl_init
  2. do_sysctl
  3. sys_sysctl
  4. in_egroup_p
  5. test_perm
  6. ctl_perm
  7. parse_table
  8. do_sysctl_strategy
  9. do_securelevel_strategy
  10. register_sysctl_table
  11. unregister_sysctl_table
  12. register_proc_table
  13. unregister_proc_table
  14. do_rw_proc
  15. proc_readsys
  16. proc_writesys
  17. proc_sys_permission
  18. proc_dostring
  19. proc_dointvec
  20. proc_dostring
  21. proc_dointvec
  22. sysctl_string
  23. do_string
  24. do_int
  25. do_struct

   1 /*
   2  * sysctl.c: General linux system control interface
   3  *
   4  * Begun 24 March 1995, Stephen Tweedie
   5  * Added /proc support, Dec 1995
   6  */
   7 
   8 #include <linux/config.h>
   9 #include <linux/sched.h>
  10 #include <linux/mm.h>
  11 #include <linux/sysctl.h>
  12 #include <linux/swapctl.h>
  13 #include <linux/proc_fs.h>
  14 #include <linux/malloc.h>
  15 #include <linux/stat.h>
  16 #include <linux/ctype.h>
  17 #include <asm/bitops.h>
  18 #include <asm/segment.h>
  19 
  20 #include <linux/utsname.h>
  21 #include <linux/swapctl.h>
  22 
  23 static ctl_table root_table[];
  24 static struct ctl_table_header root_table_header = 
  25         {root_table, DNODE_SINGLE(&root_table_header)};
  26 
  27 static int parse_table(int *, int, void *, size_t *, void *, size_t,
  28                        ctl_table *, void **);
  29 
  30 static ctl_table kern_table[];
  31 static ctl_table vm_table[];
  32 
  33 /* /proc declarations: */
  34 
  35 #ifdef CONFIG_PROC_FS
  36 
  37 static int proc_readsys(struct inode * inode, struct file * file,
  38                         char * buf, int count);
  39 static int proc_writesys(struct inode * inode, struct file * file,
  40                          const char * buf, int count);
  41 static int proc_sys_permission(struct inode *, int);
  42 
  43 struct file_operations proc_sys_file_operations =
  44 {
  45         NULL,           /* lseek   */
  46         proc_readsys,   /* read    */
  47         proc_writesys,  /* write   */
  48         NULL,           /* readdir */
  49         NULL,           /* select  */
  50         NULL,           /* ioctl   */
  51         NULL,           /* mmap    */
  52         NULL,           /* no special open code    */
  53         NULL,           /* no special release code */
  54         NULL            /* can't fsync */
  55 };
  56 
  57 struct inode_operations proc_sys_inode_operations =
  58 {
  59         &proc_sys_file_operations,
  60         NULL,           /* create */
  61         NULL,           /* lookup */
  62         NULL,           /* link */
  63         NULL,           /* unlink */
  64         NULL,           /* symlink */
  65         NULL,           /* mkdir */
  66         NULL,           /* rmdir */
  67         NULL,           /* mknod */
  68         NULL,           /* rename */
  69         NULL,           /* readlink */
  70         NULL,           /* follow_link */
  71         NULL,           /* readpage */
  72         NULL,           /* writepage */
  73         NULL,           /* bmap */
  74         NULL,           /* truncate */
  75         proc_sys_permission
  76 };
  77 
  78 extern struct proc_dir_entry proc_sys_root;
  79 
  80 static void register_proc_table(ctl_table *, struct proc_dir_entry *);
  81 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
  82 #endif
  83 
  84 static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
  85                                     void *, size_t, void **);
  86 
  87 /* The default sysctl tables: */
  88 
  89 static ctl_table root_table[] = {
  90         {CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
  91         {CTL_VM, "vm", NULL, 0, 0555, vm_table},
  92         {0}
  93 };
  94 
  95 static ctl_table kern_table[] = {
  96         {KERN_OSTYPE, "ostype", system_utsname.sysname, 64,
  97          0444, NULL, &proc_dostring, &sysctl_string},
  98         {KERN_OSRELEASE, "osrelease", system_utsname.release, 64,
  99          0444, NULL, &proc_dostring, &sysctl_string},
 100         {KERN_VERSION, "version", system_utsname.version, 64,
 101          0444, NULL, &proc_dostring, &sysctl_string},
 102         {KERN_NODENAME, "hostname", system_utsname.nodename, 64,
 103          0644, NULL, &proc_dostring, &sysctl_string},
 104         {KERN_DOMAINNAME, "domainname", system_utsname.domainname, 64,
 105          0644, NULL, &proc_dostring, &sysctl_string},
 106         {KERN_NRINODE, "inode-nr", &nr_inodes, 2*sizeof(int),
 107          0444, NULL, &proc_dointvec},
 108         {KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int),
 109          0644, NULL, &proc_dointvec},
 110         {KERN_NRFILE, "file-nr", &nr_files, sizeof(int),
 111          0444, NULL, &proc_dointvec},
 112         {KERN_MAXFILE, "file-max", &max_files, sizeof(int),
 113          0644, NULL, &proc_dointvec},
 114         {KERN_SECURELVL, "securelevel", &securelevel, sizeof(int),
 115          0444, NULL, &proc_dointvec, (ctl_handler *)&do_securelevel_strategy},
 116         {0}
 117 };
 118 
 119 static ctl_table vm_table[] = {
 120         {VM_SWAPCTL, "swapctl", 
 121          &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
 122         {VM_KSWAPD, "kswapd", 
 123          &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
 124         {0}
 125 };
 126 
 127 void sysctl_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129 #ifdef CONFIG_PROC_FS
 130         register_proc_table(root_table, &proc_sys_root);
 131 #endif
 132 }
 133 
 134 
 135 int do_sysctl (int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 136                void *oldval, size_t *oldlenp,
 137                void *newval, size_t newlen)
 138 {
 139         int error;
 140         struct ctl_table_header *tmp;
 141         void *context;
 142         
 143         if (nlen == 0 || nlen >= CTL_MAXNAME)
 144                 return -ENOTDIR;
 145         
 146         error = verify_area(VERIFY_READ,name,nlen*sizeof(int));
 147         if (error) return error;
 148         if (oldval) {
 149                 if (!oldlenp)
 150                         return -EFAULT;
 151                 error = verify_area(VERIFY_WRITE,oldlenp,sizeof(size_t));
 152                 if (error) return error;
 153                 error = verify_area(VERIFY_WRITE,oldval,get_user(oldlenp));
 154                 if (error) return error;
 155         }
 156         if (newval) {
 157                 error = verify_area(VERIFY_READ,newval,newlen);
 158                 if (error) return error;
 159         }
 160         tmp = &root_table_header;
 161         do {
 162                 context = 0;
 163                 error = parse_table(name, nlen, oldval, oldlenp, 
 164                                     newval, newlen, root_table, &context);
 165                 if (context)
 166                         kfree(context);
 167                 if (error != ENOTDIR)
 168                         return error;
 169                 tmp = tmp->DLIST_NEXT(ctl_entry);
 170         } while (tmp != &root_table_header);
 171         return -ENOTDIR;
 172 }
 173 
 174 extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
     /* [previous][next][first][last][top][bottom][index][help] */
 175 {
 176         struct __sysctl_args tmp;
 177         int error;
 178         error = verify_area(VERIFY_READ, args, sizeof(*args));
 179         if (error)
 180                 return error;
 181         memcpy_fromfs(&tmp, args, sizeof(tmp));
 182         return do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp, 
 183                          tmp.newval, tmp.newlen);
 184 }
 185 
 186 /* Like in_group_p, but testing against egid, not fsgid */
 187 static int in_egroup_p(gid_t grp)
     /* [previous][next][first][last][top][bottom][index][help] */
 188 {
 189         int     i;
 190 
 191         if (grp == current->euid)
 192                 return 1;
 193 
 194         for (i = 0; i < NGROUPS; i++) {
 195                 if (current->groups[i] == NOGROUP)
 196                         break;
 197                 if (current->groups[i] == grp)
 198                         return 1;
 199         }
 200         return 0;
 201 }
 202 /* ctl_perm does NOT grant the superuser all rights automatically, because
 203    some sysctl variables are readonly even to root. */
 204 static int test_perm(int mode, int op)
     /* [previous][next][first][last][top][bottom][index][help] */
 205 {
 206         if (!current->euid)
 207                 mode >>= 6;
 208         else if (in_egroup_p(0))
 209                 mode >>= 3;
 210         if ((mode & op & 0007) == op)
 211                 return 0;
 212         return -EACCES;
 213 }
 214 static inline int ctl_perm(ctl_table *table, int op)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216         return test_perm(table->mode, op);
 217 }
 218 
 219 static int parse_table(int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 220                        void *oldval, size_t *oldlenp,
 221                        void *newval, size_t newlen,
 222                        ctl_table *table, void **context)
 223 {
 224         int error;
 225 repeat:
 226         if (!nlen)
 227                 return -ENOTDIR;
 228 
 229         for ( ; table->ctl_name; table++) {
 230                 if (get_user(name) == table->ctl_name ||
 231                     table->ctl_name == CTL_ANY) {
 232                         if (table->child) {
 233                                 if (ctl_perm(table, 001))
 234                                         return -EPERM;
 235                                 if (table->strategy) {
 236                                         error = table->strategy(
 237                                                 table, name, nlen,
 238                                                 oldval, oldlenp,
 239                                                 newval, newlen, context);
 240                                 if (error)
 241                                         return error;
 242                                 }
 243                                 name++;
 244                                 nlen--;
 245                                 table = table->child;
 246                                 goto repeat;
 247                         }
 248                         error = do_sysctl_strategy(table, name, nlen,
 249                                                    oldval, oldlenp,
 250                                                    newval, newlen, context);
 251                         return error;
 252                 }
 253         };
 254         return -ENOTDIR;
 255 }
 256 
 257 /* Perform the actual read/write of a sysctl table entry. */
 258 int do_sysctl_strategy (ctl_table *table, 
     /* [previous][next][first][last][top][bottom][index][help] */
 259                         int *name, int nlen,
 260                         void *oldval, size_t *oldlenp,
 261                         void *newval, size_t newlen, void **context)
 262 {
 263         int op = 0, rc, len;
 264 
 265         if (oldval)
 266                 op |= 004;
 267         if (newval) 
 268                 op |= 002;
 269         if (ctl_perm(table, op))
 270                 return -EPERM;
 271 
 272         if (table->strategy) {
 273                 rc = table->strategy(table, name, nlen, oldval, oldlenp,
 274                                      newval, newlen, context);
 275                 if (rc < 0)
 276                         return rc;
 277                 if (rc > 0)
 278                         return 0;
 279         }
 280 
 281         /* If there is no strategy routine, or if the strategy returns
 282          * zero, proceed with automatic r/w */
 283         if (table->data && table->maxlen) {
 284                 if (oldval && oldlenp && get_user(oldlenp)) {
 285                         len = get_user(oldlenp);
 286                         if (len > table->maxlen)
 287                                 len = table->maxlen;
 288                         memcpy_tofs(oldval, table->data, len);
 289                         put_user(len, oldlenp);
 290                 }
 291                 if (newval && newlen) {
 292                         len = newlen;
 293                         if (len > table->maxlen)
 294                                 len = table->maxlen;
 295                         memcpy_fromfs(table->data, newval, len);
 296                 }
 297         }
 298         return 0;
 299 }
 300 
 301 /*
 302  * This function only checks permission for changing the security level
 303  * If the tests are successfull, the actual change is done by
 304  * do_sysctl_strategy
 305  */
 306 static int do_securelevel_strategy (ctl_table *table, 
     /* [previous][next][first][last][top][bottom][index][help] */
 307                                     int *name, int nlen,
 308                                     void *oldval, size_t *oldlenp,
 309                                     void *newval, size_t newlen, void **context)
 310 {
 311         int level;
 312 
 313         if (newval && newlen) {
 314                 if (newlen != sizeof (int))
 315                         return -EINVAL;
 316                 memcpy_fromfs (&level, newval, newlen);
 317                 if (level < securelevel && current->pid != 1)
 318                         return -EPERM;
 319         }
 320         return 0;
 321 }
 322 
 323 struct ctl_table_header *register_sysctl_table(ctl_table * table, 
     /* [previous][next][first][last][top][bottom][index][help] */
 324                                                int insert_at_head)
 325 {
 326         struct ctl_table_header *tmp;
 327         tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
 328         if (!tmp)
 329                 return 0;
 330         *tmp = ((struct ctl_table_header) {table, DNODE_NULL});
 331         if (insert_at_head)
 332                 DLIST_INSERT_AFTER(&root_table_header, tmp, ctl_entry);
 333         else
 334                 DLIST_INSERT_BEFORE(&root_table_header, tmp, ctl_entry);
 335 #ifdef CONFIG_PROC_FS
 336         register_proc_table(table, &proc_sys_root);
 337 #endif
 338         return tmp;
 339 }
 340 
 341 void unregister_sysctl_table(struct ctl_table_header * table)
     /* [previous][next][first][last][top][bottom][index][help] */
 342 {
 343         DLIST_DELETE(table, ctl_entry);
 344 #ifdef CONFIG_PROC_FS
 345         unregister_proc_table(table->ctl_table, &proc_sys_root);
 346 #endif
 347 }
 348 
 349 /*
 350  * /proc/sys support
 351  */
 352 
 353 #ifdef CONFIG_PROC_FS
 354 
 355 /* Scan the sysctl entries in table and add them all into /proc */
 356 static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358         struct proc_dir_entry *de;
 359         
 360         for (; table->ctl_name; table++) {
 361                 /* Can't do anything without a proc name. */
 362                 if (!table->procname)
 363                         continue;
 364                 /* Maybe we can't do anything with it... */
 365                 if (!table->proc_handler &&
 366                     !table->child)
 367                         continue;
 368                 
 369                 de = kmalloc(sizeof(*de), GFP_KERNEL);
 370                 if (!de) continue;
 371                 de->namelen = strlen(table->procname);
 372                 de->name = table->procname;
 373                 de->mode = table->mode;
 374                 de->nlink = 1;
 375                 de->uid = 0;
 376                 de->gid = 0;
 377                 de->size = 0;
 378                 de->get_info = 0;       /* For internal use if we want it */
 379                 de->fill_inode = 0;     /* To override struct inode fields */
 380                 de->next = de->subdir = 0;
 381                 de->data = (void *) table;
 382                 /* Is it a file? */
 383                 if (table->proc_handler) {
 384                         de->ops = &proc_sys_inode_operations;
 385                         de->mode |= S_IFREG;
 386                 }
 387                 /* Otherwise it's a subdir */
 388                 else  {
 389                         de->ops = &proc_dir_inode_operations;
 390                         de->nlink++;
 391                         de->mode |= S_IFDIR;
 392                 }
 393                 table->de = de;
 394                 proc_register_dynamic(root, de);
 395                 if (de->mode & S_IFDIR )
 396                         register_proc_table(table->child, de);
 397         }
 398 }
 399 
 400 static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
     /* [previous][next][first][last][top][bottom][index][help] */
 401 {
 402         struct proc_dir_entry *de;
 403         for (; table->ctl_name; table++) {
 404                 if (!(de = table->de))
 405                         continue;
 406                 if (de->mode & S_IFDIR) {
 407                         if (!table->child) {
 408                                 printk (KERN_ALERT "Help - malformed sysctl tree on free\n");
 409                                 continue;
 410                         }
 411                         unregister_proc_table(table->child, de);
 412                 }
 413                 proc_unregister(root, de->low_ino);
 414                 kfree(de);                      
 415         }
 416 }
 417 
 418 
 419 static int do_rw_proc(int write, struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 420                       char * buf, int count)
 421 {
 422         int error, op;
 423         struct proc_dir_entry *de;
 424         struct ctl_table *table;
 425         size_t res;
 426         
 427         error = verify_area(write ? VERIFY_READ : VERIFY_WRITE, buf, count);
 428         if (error)
 429                 return error;
 430 
 431         de = (struct proc_dir_entry*) inode->u.generic_ip;
 432         if (!de || !de->data)
 433                 return -ENOTDIR;
 434         table = (struct ctl_table *) de->data;
 435         if (!table || !table->proc_handler)
 436                 return -ENOTDIR;
 437         op = (write ? 002 : 004);
 438         if (ctl_perm(table, op))
 439                 return -EPERM;
 440         
 441         res = count;
 442         error = (*table->proc_handler) (table, write, file, buf, &res);
 443         if (error)
 444                 return error;
 445         return res;
 446 }
 447 
 448 static int proc_readsys(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 449                         char * buf, int count)
 450 {
 451         return do_rw_proc(0, inode, file, buf, count);
 452 }
 453 
 454 static int proc_writesys(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 455                          const char * buf, int count)
 456 {
 457         return do_rw_proc(1, inode, file, (char *) buf, count);
 458 }
 459 
 460 static int proc_sys_permission(struct inode *inode, int op)
     /* [previous][next][first][last][top][bottom][index][help] */
 461 {
 462         return test_perm(inode->i_mode, op);
 463 }
 464 
 465 int proc_dostring(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 466                   void *buffer, size_t *lenp)
 467 {
 468         int len;
 469         char *p, c;
 470         
 471         if (!table->data || !table->maxlen || !*lenp ||
 472             (filp->f_pos && !write)) {
 473                 *lenp = 0;
 474                 return 0;
 475         }
 476         
 477         if (write) {
 478                 len = 0;
 479                 p = buffer;
 480                 while (len < *lenp && 
 481                        (c = get_user(p++)) != 0 && c != '\n')
 482                         len++;
 483                 if (len >= table->maxlen)
 484                         len = table->maxlen-1;
 485                 memcpy_fromfs(table->data, buffer, len);
 486                 ((char *) table->data)[len] = 0;
 487                 filp->f_pos += *lenp;
 488         } else {
 489                 len = strlen(table->data) + 1;
 490                 if (len > table->maxlen)
 491                         len = table->maxlen;
 492                 if (len > *lenp)
 493                         len = *lenp;
 494                 if (len) {                      
 495                         memcpy_tofs(buffer, table->data, len-1);
 496                         put_user(0, ((char *) buffer) + len - 1);
 497                 }
 498                 if (len < *lenp) {
 499                         put_user('\n', ((char *) buffer) + len);
 500                         len++;
 501                 }
 502                 *lenp = len;
 503                 filp->f_pos += len;
 504         }
 505         return 0;
 506 }
 507 
 508 int proc_dointvec(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 509                   void *buffer, size_t *lenp)
 510 {
 511         int *i, vleft, first=1, len, left, neg, val;
 512         #define TMPBUFLEN 20
 513         char buf[TMPBUFLEN], *p;
 514         
 515         if (!table->data || !table->maxlen || !*lenp ||
 516             (filp->f_pos && !write)) {
 517                 *lenp = 0;
 518                 return 0;
 519         }
 520         
 521         i = (int *) table->data;
 522         vleft = table->maxlen / sizeof(int);
 523         left = *lenp;
 524         
 525         for (; left && vleft--; i++, first=0) {
 526                 if (write) {
 527                         while (left && isspace(get_user((char *) buffer)))
 528                                 left--, ((char *) buffer)++;
 529                         if (!left)
 530                                 break;
 531                         neg = 0;
 532                         len = left;
 533                         if (len > TMPBUFLEN-1)
 534                                 len = TMPBUFLEN-1;
 535                         memcpy_fromfs(buf, buffer, len);
 536                         buf[len] = 0;
 537                         p = buf;
 538                         if (*p == '-' && left > 1) {
 539                                 neg = 1;
 540                                 left--, p++;
 541                         }
 542                         if (*p < '0' || *p > '9')
 543                                 break;
 544                         val = simple_strtoul(p, &p, 0);
 545                         len = p-buf;
 546                         if ((len < left) && *p && !isspace(*p))
 547                                 break;
 548                         if (neg)
 549                                 val = -val;
 550                         buffer += len;
 551                         left -= len;
 552                         *i = val;
 553                 } else {
 554                         p = buf;
 555                         if (!first)
 556                                 *p++ = '\t';
 557                         sprintf(p, "%d", *i);
 558                         len = strlen(buf);
 559                         if (len > left)
 560                                 len = left;
 561                         memcpy_tofs(buffer, buf, len);
 562                         left -= len;
 563                         buffer += len;
 564                 }
 565         }
 566 
 567         if (!write && !first && left) {
 568                 put_user('\n', (char *) buffer);
 569                 left--, buffer++;
 570         }
 571         if (write) {
 572                 p = (char *) buffer;
 573                 while (left && isspace(get_user(p++)))
 574                         left--;
 575         }
 576         if (write && first)
 577                 return -EINVAL;
 578         *lenp -= left;
 579         filp->f_pos += *lenp;
 580         return 0;
 581 }
 582 
 583 #else /* CONFIG_PROC_FS */
 584 
 585 int proc_dostring(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 586                   void *buffer, size_t *lenp)
 587 {
 588         return -ENOSYS;
 589 }
 590 
 591 int proc_dointvec(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 592                   void *buffer, size_t *lenp)
 593 {
 594         return -ENOSYS;
 595 }
 596 
 597 #endif /* CONFIG_PROC_FS */
 598 
 599 
 600 /*
 601  * General sysctl support routines 
 602  */
 603 
 604 /* The generic string strategy routine: */
 605 int sysctl_string(ctl_table *table, int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 606                   void *oldval, size_t *oldlenp,
 607                   void *newval, size_t newlen, void **context)
 608 {
 609         int l, len;
 610         
 611         if (!table->data || !table->maxlen) 
 612                 return -ENOTDIR;
 613         
 614         if (oldval && oldlenp && get_user(oldlenp)) {
 615                 len = get_user(oldlenp);
 616                 l = strlen(table->data);
 617                 if (len > l) len = l;
 618                 if (len >= table->maxlen)
 619                         len = table->maxlen;
 620                 memcpy_tofs(oldval, table->data, len);
 621                 put_user(0, ((char *) oldval) + len);
 622                 put_user(len, oldlenp);
 623         }
 624         if (newval && newlen) {
 625                 len = newlen;
 626                 if (len > table->maxlen)
 627                         len = table->maxlen;
 628                 memcpy_fromfs(table->data, newval, len);
 629                 if (len == table->maxlen)
 630                         len--;
 631                 ((char *) table->data)[len] = 0;
 632         }
 633         return 0;
 634 }
 635 
 636 int do_string (
     /* [previous][next][first][last][top][bottom][index][help] */
 637         void *oldval, size_t *oldlenp, void *newval, size_t newlen,
 638         int rdwr, char *data, size_t max)
 639 {
 640         int l = strlen(data) + 1;
 641         if (newval && !rdwr)
 642                 return -EPERM;
 643         if (newval && newlen >= max)
 644                 return -EINVAL;
 645         if (oldval) {
 646                 if (l > get_user(oldlenp))
 647                         return -ENOMEM;
 648                 put_user(l, oldlenp);
 649                 memcpy_tofs(oldval, data, l);
 650         }
 651         if (newval) {
 652                 memcpy_fromfs(data, newval, newlen);
 653                 data[newlen] = 0;
 654         }
 655         return 0;
 656 }
 657 
 658 int do_int (
     /* [previous][next][first][last][top][bottom][index][help] */
 659         void *oldval, size_t *oldlenp, void *newval, size_t newlen,
 660         int rdwr, int *data)
 661 {
 662         if (newval && !rdwr)
 663                 return -EPERM;
 664         if (newval && newlen != sizeof(int))
 665                 return -EINVAL;
 666         if (oldval) {
 667                 if (get_user(oldlenp) < sizeof(int))
 668                         return -ENOMEM;
 669                 put_user(sizeof(int), oldlenp);
 670                 memcpy_tofs(oldval, data, sizeof(int));
 671         }
 672         if (newval)
 673                 memcpy_fromfs(data, newval, sizeof(int));
 674         return 0;
 675 }
 676 
 677 int do_struct (
     /* [previous][next][first][last][top][bottom][index][help] */
 678         void *oldval, size_t *oldlenp, void *newval, size_t newlen,
 679         int rdwr, void *data, size_t len)
 680 {
 681         if (newval && !rdwr)
 682                 return -EPERM;
 683         if (newval && newlen != len)
 684                 return -EINVAL;
 685         if (oldval) {
 686                 if (get_user(oldlenp) < len)
 687                         return -ENOMEM;
 688                 put_user(len, oldlenp);
 689                 memcpy_tofs(oldval, data, len);
 690         }
 691         if (newval)
 692                 memcpy_fromfs(data, newval, len);
 693         return 0;
 694 }
 695 

/* [previous][next][first][last][top][bottom][index][help] */