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  * Added hooks for /proc/sys/net (minor, minor patch), 96/4/1, Mike Shaver.
   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 /* External variables not in a header file. */
  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 /* /proc declarations: */
  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,           /* lseek   */
  57         proc_readsys,   /* read    */
  58         proc_writesys,  /* write   */
  59         NULL,           /* readdir */
  60         NULL,           /* select  */
  61         NULL,           /* ioctl   */
  62         NULL,           /* mmap    */
  63         NULL,           /* no special open code    */
  64         NULL,           /* no special release code */
  65         NULL            /* can't fsync */
  66 };
  67 
  68 struct inode_operations proc_sys_inode_operations =
  69 {
  70         &proc_sys_file_operations,
  71         NULL,           /* create */
  72         NULL,           /* lookup */
  73         NULL,           /* link */
  74         NULL,           /* unlink */
  75         NULL,           /* symlink */
  76         NULL,           /* mkdir */
  77         NULL,           /* rmdir */
  78         NULL,           /* mknod */
  79         NULL,           /* rename */
  80         NULL,           /* readlink */
  81         NULL,           /* follow_link */
  82         NULL,           /* readpage */
  83         NULL,           /* writepage */
  84         NULL,           /* bmap */
  85         NULL,           /* truncate */
  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 /* The default sysctl tables: */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* Like in_group_p, but testing against egid, not fsgid */
 218 static int in_egroup_p(gid_t grp)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* ctl_perm does NOT grant the superuser all rights automatically, because
 234    some sysctl variables are readonly even to root. */
 235 static int test_perm(int mode, int op)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 246 {
 247         return test_perm(table->mode, op);
 248 }
 249 
 250 static int parse_table(int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* Perform the actual read/write of a sysctl table entry. */
 289 int do_sysctl_strategy (ctl_table *table, 
     /* [previous][next][first][last][top][bottom][index][help] */
 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         /* If there is no strategy routine, or if the strategy returns
 313          * zero, proceed with automatic r/w */
 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  * This function only checks permission for changing the security level
 334  * If the tests are successful, the actual change is done by
 335  * do_sysctl_strategy
 336  */
 337 static int do_securelevel_strategy (ctl_table *table, 
     /* [previous][next][first][last][top][bottom][index][help] */
 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, 
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * /proc/sys support
 382  */
 383 
 384 #ifdef CONFIG_PROC_FS
 385 
 386 /* Scan the sysctl entries in table and add them all into /proc */
 387 static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
     /* [previous][next][first][last][top][bottom][index][help] */
 388 {
 389         struct proc_dir_entry *de;
 390         
 391         for (; table->ctl_name; table++) {
 392                 /* Can't do anything without a proc name. */
 393                 if (!table->procname)
 394                         continue;
 395                 /* Maybe we can't do anything with it... */
 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;       /* For internal use if we want it */
 410                 de->fill_inode = 0;     /* To override struct inode fields */
 411                 de->next = de->subdir = 0;
 412                 de->data = (void *) table;
 413                 /* Is it a file? */
 414                 if (table->proc_handler) {
 415                         de->ops = &proc_sys_inode_operations;
 416                         de->mode |= S_IFREG;
 417                 }
 418                 /* Otherwise it's a subdir */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 492 {
 493         return test_perm(inode->i_mode, op);
 494 }
 495 
 496 int proc_dostring(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 /* CONFIG_PROC_FS */
 697 
 698 int proc_dostring(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 699                   void *buffer, size_t *lenp)
 700 {
 701         return -ENOSYS;
 702 }
 703 
 704 int proc_dointvec(ctl_table *table, int write, struct file *filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 711                     void *buffer, size_t *lenp)
 712 {
 713         return -ENOSYS;
 714 }
 715 
 716 #endif /* CONFIG_PROC_FS */
 717 
 718 
 719 /*
 720  * General sysctl support routines 
 721  */
 722 
 723 /* The generic string strategy routine: */
 724 int sysctl_string(ctl_table *table, int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * This function makes sure that all of the integers in the vector
 757  * are between the minimum and maximum values given in the arrays
 758  * table->extra1 and table->extra2, respectively.
 759  */
 760 int sysctl_intvec(ctl_table *table, int *name, int nlen,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 (
     /* [previous][next][first][last][top][bottom][index][help] */
 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 (
     /* [previous][next][first][last][top][bottom][index][help] */
 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 (
     /* [previous][next][first][last][top][bottom][index][help] */
 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 

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