root/kernel/sysctl.c

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

DEFINITIONS

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

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

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