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

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