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

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