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_dointvec_minmax
  21. proc_dostring
  22. proc_dointvec
  23. proc_dointvec_minmax
  24. sysctl_string
  25. sysctl_intvec
  26. do_string
  27. do_int
  28. 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  * Added bdflush entry and intvec min/max checking, 2/23/96, Tom Dyas.
   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 /* External variables not in a header file. */
  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 /* /proc declarations: */
  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,           /* lseek   */
  51         proc_readsys,   /* read    */
  52         proc_writesys,  /* write   */
  53         NULL,           /* readdir */
  54         NULL,           /* select  */
  55         NULL,           /* ioctl   */
  56         NULL,           /* mmap    */
  57         NULL,           /* no special open code    */
  58         NULL,           /* no special release code */
  59         NULL            /* can't fsync */
  60 };
  61 
  62 struct inode_operations proc_sys_inode_operations =
  63 {
  64         &proc_sys_file_operations,
  65         NULL,           /* create */
  66         NULL,           /* lookup */
  67         NULL,           /* link */
  68         NULL,           /* unlink */
  69         NULL,           /* symlink */
  70         NULL,           /* mkdir */
  71         NULL,           /* rmdir */
  72         NULL,           /* mknod */
  73         NULL,           /* rename */
  74         NULL,           /* readlink */
  75         NULL,           /* follow_link */
  76         NULL,           /* readpage */
  77         NULL,           /* writepage */
  78         NULL,           /* bmap */
  79         NULL,           /* truncate */
  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 /* The default sysctl tables: */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* Like in_group_p, but testing against egid, not fsgid */
 201 static int in_egroup_p(gid_t grp)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* ctl_perm does NOT grant the superuser all rights automatically, because
 217    some sysctl variables are readonly even to root. */
 218 static int test_perm(int mode, int op)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230         return test_perm(table->mode, op);
 231 }
 232 
 233 static int parse_table(int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* Perform the actual read/write of a sysctl table entry. */
 272 int do_sysctl_strategy (ctl_table *table, 
     /* [previous][next][first][last][top][bottom][index][help] */
 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         /* If there is no strategy routine, or if the strategy returns
 296          * zero, proceed with automatic r/w */
 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  * This function only checks permission for changing the security level
 317  * If the tests are successfull, the actual change is done by
 318  * do_sysctl_strategy
 319  */
 320 static int do_securelevel_strategy (ctl_table *table, 
     /* [previous][next][first][last][top][bottom][index][help] */
 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, 
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * /proc/sys support
 365  */
 366 
 367 #ifdef CONFIG_PROC_FS
 368 
 369 /* Scan the sysctl entries in table and add them all into /proc */
 370 static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
     /* [previous][next][first][last][top][bottom][index][help] */
 371 {
 372         struct proc_dir_entry *de;
 373         
 374         for (; table->ctl_name; table++) {
 375                 /* Can't do anything without a proc name. */
 376                 if (!table->procname)
 377                         continue;
 378                 /* Maybe we can't do anything with it... */
 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;       /* For internal use if we want it */
 393                 de->fill_inode = 0;     /* To override struct inode fields */
 394                 de->next = de->subdir = 0;
 395                 de->data = (void *) table;
 396                 /* Is it a file? */
 397                 if (table->proc_handler) {
 398                         de->ops = &proc_sys_inode_operations;
 399                         de->mode |= S_IFREG;
 400                 }
 401                 /* Otherwise it's a subdir */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 475 {
 476         return test_perm(inode->i_mode, op);
 477 }
 478 
 479 int proc_dostring(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* CONFIG_PROC_FS */
 680 
 681 int proc_dostring(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 682                   void *buffer, size_t *lenp)
 683 {
 684         return -ENOSYS;
 685 }
 686 
 687 int proc_dointvec(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 694                     void *buffer, size_t *lenp)
 695 {
 696         return -ENOSYS;
 697 }
 698 
 699 #endif /* CONFIG_PROC_FS */
 700 
 701 
 702 /*
 703  * General sysctl support routines 
 704  */
 705 
 706 /* The generic string strategy routine: */
 707 int sysctl_string(ctl_table *table, int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * This function makes sure that all of the integers in the vector
 740  * are between the minimum and maximum values given in the arrays
 741  * table->extra1 and table->extra2, respectively.
 742  */
 743 int sysctl_intvec(ctl_table *table, int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 (
     /* [previous][next][first][last][top][bottom][index][help] */
 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 (
     /* [previous][next][first][last][top][bottom][index][help] */
 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 (
     /* [previous][next][first][last][top][bottom][index][help] */
 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 

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