/*
|
/*
|
* sysctl.c: General linux system control interface
|
* sysctl.c: General linux system control interface
|
*
|
*
|
* Begun 24 March 1995, Stephen Tweedie
|
* Begun 24 March 1995, Stephen Tweedie
|
* Added /proc support, Dec 1995
|
* Added /proc support, Dec 1995
|
* Added bdflush entry and intvec min/max checking, 2/23/96, Tom Dyas.
|
* Added bdflush entry and intvec min/max checking, 2/23/96, Tom Dyas.
|
* Added hooks for /proc/sys/net (minor, minor patch), 96/4/1, Mike Shaver.
|
* Added hooks for /proc/sys/net (minor, minor patch), 96/4/1, Mike Shaver.
|
* Added kernel/java-{interpreter,appletviewer}, 96/5/10, Mike Shaver.
|
* Added kernel/java-{interpreter,appletviewer}, 96/5/10, Mike Shaver.
|
*/
|
*/
|
|
|
/*
|
/*
|
* uClinux revisions for NO_MM
|
* uClinux revisions for NO_MM
|
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
|
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
|
* The Silver Hammer Group, Ltd.
|
* The Silver Hammer Group, Ltd.
|
*/
|
*/
|
|
|
#include <linux/config.h>
|
#include <linux/config.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
#include <linux/sysctl.h>
|
#include <linux/sysctl.h>
|
#include <linux/swapctl.h>
|
#include <linux/swapctl.h>
|
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/stat.h>
|
#include <linux/stat.h>
|
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
#include <asm/bitops.h>
|
#include <asm/bitops.h>
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
#include <linux/swapctl.h>
|
#include <linux/swapctl.h>
|
|
|
/* External variables not in a header file. */
|
/* External variables not in a header file. */
|
extern int panic_timeout;
|
extern int panic_timeout;
|
|
|
|
|
#ifdef CONFIG_ROOT_NFS
|
#ifdef CONFIG_ROOT_NFS
|
#include <linux/nfs_fs.h>
|
#include <linux/nfs_fs.h>
|
#endif
|
#endif
|
|
|
static ctl_table root_table[];
|
static ctl_table root_table[];
|
static struct ctl_table_header root_table_header =
|
static struct ctl_table_header root_table_header =
|
{root_table, DNODE_SINGLE(&root_table_header)};
|
{root_table, DNODE_SINGLE(&root_table_header)};
|
|
|
static int parse_table(int *, int, void *, size_t *, void *, size_t,
|
static int parse_table(int *, int, void *, size_t *, void *, size_t,
|
ctl_table *, void **);
|
ctl_table *, void **);
|
|
|
static ctl_table kern_table[];
|
static ctl_table kern_table[];
|
static ctl_table vm_table[];
|
static ctl_table vm_table[];
|
extern ctl_table net_table[];
|
extern ctl_table net_table[];
|
|
|
/* /proc declarations: */
|
/* /proc declarations: */
|
|
|
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
|
|
static int proc_readsys(struct inode * inode, struct file * file,
|
static int proc_readsys(struct inode * inode, struct file * file,
|
char * buf, int count);
|
char * buf, int count);
|
static int proc_writesys(struct inode * inode, struct file * file,
|
static int proc_writesys(struct inode * inode, struct file * file,
|
const char * buf, int count);
|
const char * buf, int count);
|
static int proc_sys_permission(struct inode *, int);
|
static int proc_sys_permission(struct inode *, int);
|
|
|
struct file_operations proc_sys_file_operations =
|
struct file_operations proc_sys_file_operations =
|
{
|
{
|
NULL, /* lseek */
|
NULL, /* lseek */
|
proc_readsys, /* read */
|
proc_readsys, /* read */
|
proc_writesys, /* write */
|
proc_writesys, /* write */
|
NULL, /* readdir */
|
NULL, /* readdir */
|
NULL, /* select */
|
NULL, /* select */
|
NULL, /* ioctl */
|
NULL, /* ioctl */
|
NULL, /* mmap */
|
NULL, /* mmap */
|
NULL, /* no special open code */
|
NULL, /* no special open code */
|
NULL, /* no special release code */
|
NULL, /* no special release code */
|
NULL /* can't fsync */
|
NULL /* can't fsync */
|
};
|
};
|
|
|
struct inode_operations proc_sys_inode_operations =
|
struct inode_operations proc_sys_inode_operations =
|
{
|
{
|
&proc_sys_file_operations,
|
&proc_sys_file_operations,
|
NULL, /* create */
|
NULL, /* create */
|
NULL, /* lookup */
|
NULL, /* lookup */
|
NULL, /* link */
|
NULL, /* link */
|
NULL, /* unlink */
|
NULL, /* unlink */
|
NULL, /* symlink */
|
NULL, /* symlink */
|
NULL, /* mkdir */
|
NULL, /* mkdir */
|
NULL, /* rmdir */
|
NULL, /* rmdir */
|
NULL, /* mknod */
|
NULL, /* mknod */
|
NULL, /* rename */
|
NULL, /* rename */
|
NULL, /* readlink */
|
NULL, /* readlink */
|
NULL, /* follow_link */
|
NULL, /* follow_link */
|
NULL, /* readpage */
|
NULL, /* readpage */
|
NULL, /* writepage */
|
NULL, /* writepage */
|
NULL, /* bmap */
|
NULL, /* bmap */
|
NULL, /* truncate */
|
NULL, /* truncate */
|
proc_sys_permission
|
proc_sys_permission
|
};
|
};
|
|
|
extern struct proc_dir_entry proc_sys_root;
|
extern struct proc_dir_entry proc_sys_root;
|
|
|
static void register_proc_table(ctl_table *, struct proc_dir_entry *);
|
static void register_proc_table(ctl_table *, struct proc_dir_entry *);
|
static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
|
static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
|
#endif
|
#endif
|
|
|
extern int bdf_prm[], bdflush_min[], bdflush_max[];
|
extern int bdf_prm[], bdflush_min[], bdflush_max[];
|
|
|
static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
|
static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
|
void *, size_t, void **);
|
void *, size_t, void **);
|
|
|
extern char binfmt_java_interpreter[], binfmt_java_appletviewer[];
|
extern char binfmt_java_interpreter[], binfmt_java_appletviewer[];
|
|
|
/* The default sysctl tables: */
|
/* The default sysctl tables: */
|
|
|
static ctl_table root_table[] = {
|
static ctl_table root_table[] = {
|
{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
|
{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
|
{CTL_VM, "vm", NULL, 0, 0555, vm_table},
|
{CTL_VM, "vm", NULL, 0, 0555, vm_table},
|
{CTL_NET, "net", NULL, 0, 0555, net_table},
|
{CTL_NET, "net", NULL, 0, 0555, net_table},
|
{0}
|
{0}
|
};
|
};
|
|
|
static ctl_table kern_table[] = {
|
static ctl_table kern_table[] = {
|
{KERN_OSTYPE, "ostype", system_utsname.sysname, 64,
|
{KERN_OSTYPE, "ostype", system_utsname.sysname, 64,
|
0444, NULL, &proc_dostring, &sysctl_string},
|
0444, NULL, &proc_dostring, &sysctl_string},
|
{KERN_OSRELEASE, "osrelease", system_utsname.release, 64,
|
{KERN_OSRELEASE, "osrelease", system_utsname.release, 64,
|
0444, NULL, &proc_dostring, &sysctl_string},
|
0444, NULL, &proc_dostring, &sysctl_string},
|
{KERN_VERSION, "version", system_utsname.version, 64,
|
{KERN_VERSION, "version", system_utsname.version, 64,
|
0444, NULL, &proc_dostring, &sysctl_string},
|
0444, NULL, &proc_dostring, &sysctl_string},
|
{KERN_NODENAME, "hostname", system_utsname.nodename, 64,
|
{KERN_NODENAME, "hostname", system_utsname.nodename, 64,
|
0644, NULL, &proc_dostring, &sysctl_string},
|
0644, NULL, &proc_dostring, &sysctl_string},
|
{KERN_DOMAINNAME, "domainname", system_utsname.domainname, 64,
|
{KERN_DOMAINNAME, "domainname", system_utsname.domainname, 64,
|
0644, NULL, &proc_dostring, &sysctl_string},
|
0644, NULL, &proc_dostring, &sysctl_string},
|
{KERN_NRINODE, "inode-nr", &nr_inodes, 2*sizeof(int),
|
{KERN_NRINODE, "inode-nr", &nr_inodes, 2*sizeof(int),
|
0444, NULL, &proc_dointvec},
|
0444, NULL, &proc_dointvec},
|
{KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int),
|
{KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int),
|
0644, NULL, &proc_dointvec},
|
0644, NULL, &proc_dointvec},
|
{KERN_NRFILE, "file-nr", &nr_files, sizeof(int),
|
{KERN_NRFILE, "file-nr", &nr_files, sizeof(int),
|
0444, NULL, &proc_dointvec},
|
0444, NULL, &proc_dointvec},
|
{KERN_MAXFILE, "file-max", &max_files, sizeof(int),
|
{KERN_MAXFILE, "file-max", &max_files, sizeof(int),
|
0644, NULL, &proc_dointvec},
|
0644, NULL, &proc_dointvec},
|
{KERN_SECURELVL, "securelevel", &securelevel, sizeof(int),
|
{KERN_SECURELVL, "securelevel", &securelevel, sizeof(int),
|
0444, NULL, &proc_dointvec, (ctl_handler *)&do_securelevel_strategy},
|
0444, NULL, &proc_dointvec, (ctl_handler *)&do_securelevel_strategy},
|
{KERN_PANIC, "panic", &panic_timeout, sizeof(int),
|
{KERN_PANIC, "panic", &panic_timeout, sizeof(int),
|
0644, NULL, &proc_dointvec},
|
0644, NULL, &proc_dointvec},
|
#ifdef CONFIG_BLK_DEV_INITRD
|
#ifdef CONFIG_BLK_DEV_INITRD
|
{KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(int),
|
{KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(int),
|
0644, NULL, &proc_dointvec},
|
0644, NULL, &proc_dointvec},
|
#endif
|
#endif
|
#ifdef CONFIG_ROOT_NFS
|
#ifdef CONFIG_ROOT_NFS
|
{KERN_NFSRNAME, "nfs-root-name", nfs_root_name, NFS_ROOT_NAME_LEN,
|
{KERN_NFSRNAME, "nfs-root-name", nfs_root_name, NFS_ROOT_NAME_LEN,
|
0644, NULL, &proc_dostring, &sysctl_string },
|
0644, NULL, &proc_dostring, &sysctl_string },
|
{KERN_NFSRADDRS, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
|
{KERN_NFSRADDRS, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
|
0644, NULL, &proc_dostring, &sysctl_string },
|
0644, NULL, &proc_dostring, &sysctl_string },
|
#endif
|
#endif
|
#ifdef CONFIG_BINFMT_JAVA
|
#ifdef CONFIG_BINFMT_JAVA
|
{KERN_JAVA_INTERPRETER, "java-interpreter", binfmt_java_interpreter,
|
{KERN_JAVA_INTERPRETER, "java-interpreter", binfmt_java_interpreter,
|
64, 0644, NULL, &proc_dostring, &sysctl_string },
|
64, 0644, NULL, &proc_dostring, &sysctl_string },
|
{KERN_JAVA_APPLETVIEWER, "java-appletviewer", binfmt_java_appletviewer,
|
{KERN_JAVA_APPLETVIEWER, "java-appletviewer", binfmt_java_appletviewer,
|
64, 0644, NULL, &proc_dostring, &sysctl_string },
|
64, 0644, NULL, &proc_dostring, &sysctl_string },
|
#endif
|
#endif
|
{0}
|
{0}
|
};
|
};
|
|
|
static ctl_table vm_table[] = {
|
static ctl_table vm_table[] = {
|
#ifndef NO_MM
|
#ifndef NO_MM
|
{VM_SWAPCTL, "swapctl",
|
{VM_SWAPCTL, "swapctl",
|
&swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
|
&swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
|
{VM_KSWAPD, "kswapd",
|
{VM_KSWAPD, "kswapd",
|
&kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
|
&kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
|
{VM_FREEPG, "freepages",
|
{VM_FREEPG, "freepages",
|
&min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
|
&min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
|
{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
|
{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
|
&proc_dointvec_minmax, &sysctl_intvec, NULL,
|
&proc_dointvec_minmax, &sysctl_intvec, NULL,
|
&bdflush_min, &bdflush_max},
|
&bdflush_min, &bdflush_max},
|
#endif /* !NO_MM */
|
#endif /* !NO_MM */
|
{0}
|
{0}
|
};
|
};
|
|
|
void sysctl_init(void)
|
void sysctl_init(void)
|
{
|
{
|
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
register_proc_table(root_table, &proc_sys_root);
|
register_proc_table(root_table, &proc_sys_root);
|
#endif
|
#endif
|
}
|
}
|
|
|
|
|
int do_sysctl (int *name, int nlen,
|
int do_sysctl (int *name, int nlen,
|
void *oldval, size_t *oldlenp,
|
void *oldval, size_t *oldlenp,
|
void *newval, size_t newlen)
|
void *newval, size_t newlen)
|
{
|
{
|
int error;
|
int error;
|
struct ctl_table_header *tmp;
|
struct ctl_table_header *tmp;
|
void *context;
|
void *context;
|
|
|
if (nlen <= 0 || nlen >= CTL_MAXNAME)
|
if (nlen <= 0 || nlen >= CTL_MAXNAME)
|
return -ENOTDIR;
|
return -ENOTDIR;
|
|
|
error = verify_area(VERIFY_READ,name,nlen*sizeof(int));
|
error = verify_area(VERIFY_READ,name,nlen*sizeof(int));
|
if (error) return error;
|
if (error) return error;
|
if (oldval) {
|
if (oldval) {
|
if (!oldlenp)
|
if (!oldlenp)
|
return -EFAULT;
|
return -EFAULT;
|
error = verify_area(VERIFY_WRITE,oldlenp,sizeof(size_t));
|
error = verify_area(VERIFY_WRITE,oldlenp,sizeof(size_t));
|
if (error) return error;
|
if (error) return error;
|
error = verify_area(VERIFY_WRITE,oldval,get_user(oldlenp));
|
error = verify_area(VERIFY_WRITE,oldval,get_user(oldlenp));
|
if (error) return error;
|
if (error) return error;
|
}
|
}
|
if (newval) {
|
if (newval) {
|
error = verify_area(VERIFY_READ,newval,newlen);
|
error = verify_area(VERIFY_READ,newval,newlen);
|
if (error) return error;
|
if (error) return error;
|
}
|
}
|
tmp = &root_table_header;
|
tmp = &root_table_header;
|
do {
|
do {
|
context = 0;
|
context = 0;
|
error = parse_table(name, nlen, oldval, oldlenp,
|
error = parse_table(name, nlen, oldval, oldlenp,
|
newval, newlen, tmp->ctl_table, &context);
|
newval, newlen, tmp->ctl_table, &context);
|
if (context)
|
if (context)
|
kfree(context);
|
kfree(context);
|
if (error != -ENOTDIR)
|
if (error != -ENOTDIR)
|
return error;
|
return error;
|
tmp = tmp->DLIST_NEXT(ctl_entry);
|
tmp = tmp->DLIST_NEXT(ctl_entry);
|
} while (tmp != &root_table_header);
|
} while (tmp != &root_table_header);
|
return -ENOTDIR;
|
return -ENOTDIR;
|
}
|
}
|
|
|
extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
|
extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
|
{
|
{
|
struct __sysctl_args tmp;
|
struct __sysctl_args tmp;
|
int error;
|
int error;
|
error = verify_area(VERIFY_READ, args, sizeof(*args));
|
error = verify_area(VERIFY_READ, args, sizeof(*args));
|
if (error)
|
if (error)
|
return error;
|
return error;
|
memcpy_fromfs(&tmp, args, sizeof(tmp));
|
memcpy_fromfs(&tmp, args, sizeof(tmp));
|
return do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
|
return do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
|
tmp.newval, tmp.newlen);
|
tmp.newval, tmp.newlen);
|
}
|
}
|
|
|
/* Like in_group_p, but testing against egid, not fsgid */
|
/* Like in_group_p, but testing against egid, not fsgid */
|
static int in_egroup_p(gid_t grp)
|
static int in_egroup_p(gid_t grp)
|
{
|
{
|
int i;
|
int i;
|
|
|
if (grp == current->egid)
|
if (grp == current->egid)
|
return 1;
|
return 1;
|
|
|
for (i = 0; i < NGROUPS; i++) {
|
for (i = 0; i < NGROUPS; i++) {
|
if (current->groups[i] == NOGROUP)
|
if (current->groups[i] == NOGROUP)
|
break;
|
break;
|
if (current->groups[i] == grp)
|
if (current->groups[i] == grp)
|
return 1;
|
return 1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
/* ctl_perm does NOT grant the superuser all rights automatically, because
|
/* ctl_perm does NOT grant the superuser all rights automatically, because
|
some sysctl variables are readonly even to root. */
|
some sysctl variables are readonly even to root. */
|
static int test_perm(int mode, int op)
|
static int test_perm(int mode, int op)
|
{
|
{
|
if (!current->euid)
|
if (!current->euid)
|
mode >>= 6;
|
mode >>= 6;
|
else if (in_egroup_p(0))
|
else if (in_egroup_p(0))
|
mode >>= 3;
|
mode >>= 3;
|
if ((mode & op & 0007) == op)
|
if ((mode & op & 0007) == op)
|
return 0;
|
return 0;
|
return -EACCES;
|
return -EACCES;
|
}
|
}
|
static inline int ctl_perm(ctl_table *table, int op)
|
static inline int ctl_perm(ctl_table *table, int op)
|
{
|
{
|
return test_perm(table->mode, op);
|
return test_perm(table->mode, op);
|
}
|
}
|
|
|
static int parse_table(int *name, int nlen,
|
static int parse_table(int *name, int nlen,
|
void *oldval, size_t *oldlenp,
|
void *oldval, size_t *oldlenp,
|
void *newval, size_t newlen,
|
void *newval, size_t newlen,
|
ctl_table *table, void **context)
|
ctl_table *table, void **context)
|
{
|
{
|
int error;
|
int error;
|
repeat:
|
repeat:
|
if (!nlen)
|
if (!nlen)
|
return -ENOTDIR;
|
return -ENOTDIR;
|
|
|
for ( ; table->ctl_name; table++) {
|
for ( ; table->ctl_name; table++) {
|
if (get_user(name) == table->ctl_name ||
|
if (get_user(name) == table->ctl_name ||
|
table->ctl_name == CTL_ANY) {
|
table->ctl_name == CTL_ANY) {
|
if (table->child) {
|
if (table->child) {
|
if (ctl_perm(table, 001))
|
if (ctl_perm(table, 001))
|
return -EPERM;
|
return -EPERM;
|
if (table->strategy) {
|
if (table->strategy) {
|
error = table->strategy(
|
error = table->strategy(
|
table, name, nlen,
|
table, name, nlen,
|
oldval, oldlenp,
|
oldval, oldlenp,
|
newval, newlen, context);
|
newval, newlen, context);
|
if (error)
|
if (error)
|
return error;
|
return error;
|
}
|
}
|
name++;
|
name++;
|
nlen--;
|
nlen--;
|
table = table->child;
|
table = table->child;
|
goto repeat;
|
goto repeat;
|
}
|
}
|
error = do_sysctl_strategy(table, name, nlen,
|
error = do_sysctl_strategy(table, name, nlen,
|
oldval, oldlenp,
|
oldval, oldlenp,
|
newval, newlen, context);
|
newval, newlen, context);
|
return error;
|
return error;
|
}
|
}
|
};
|
};
|
return -ENOTDIR;
|
return -ENOTDIR;
|
}
|
}
|
|
|
/* Perform the actual read/write of a sysctl table entry. */
|
/* Perform the actual read/write of a sysctl table entry. */
|
int do_sysctl_strategy (ctl_table *table,
|
int do_sysctl_strategy (ctl_table *table,
|
int *name, int nlen,
|
int *name, int nlen,
|
void *oldval, size_t *oldlenp,
|
void *oldval, size_t *oldlenp,
|
void *newval, size_t newlen, void **context)
|
void *newval, size_t newlen, void **context)
|
{
|
{
|
int op = 0, rc, len;
|
int op = 0, rc, len;
|
|
|
if (oldval)
|
if (oldval)
|
op |= 004;
|
op |= 004;
|
if (newval)
|
if (newval)
|
op |= 002;
|
op |= 002;
|
if (ctl_perm(table, op))
|
if (ctl_perm(table, op))
|
if( table->data != &securelevel || current->euid)
|
if( table->data != &securelevel || current->euid)
|
return -EPERM;
|
return -EPERM;
|
|
|
if (table->strategy) {
|
if (table->strategy) {
|
rc = table->strategy(table, name, nlen, oldval, oldlenp,
|
rc = table->strategy(table, name, nlen, oldval, oldlenp,
|
newval, newlen, context);
|
newval, newlen, context);
|
if (rc < 0)
|
if (rc < 0)
|
return rc;
|
return rc;
|
if (rc > 0)
|
if (rc > 0)
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* If there is no strategy routine, or if the strategy returns
|
/* If there is no strategy routine, or if the strategy returns
|
* zero, proceed with automatic r/w */
|
* zero, proceed with automatic r/w */
|
if (table->data && table->maxlen) {
|
if (table->data && table->maxlen) {
|
if (oldval && oldlenp && get_user(oldlenp)) {
|
if (oldval && oldlenp && get_user(oldlenp)) {
|
len = get_user(oldlenp);
|
len = get_user(oldlenp);
|
if (len > table->maxlen)
|
if (len > table->maxlen)
|
len = table->maxlen;
|
len = table->maxlen;
|
memcpy_tofs(oldval, table->data, len);
|
memcpy_tofs(oldval, table->data, len);
|
put_user(len, oldlenp);
|
put_user(len, oldlenp);
|
}
|
}
|
if (newval && newlen) {
|
if (newval && newlen) {
|
len = newlen;
|
len = newlen;
|
if (len > table->maxlen)
|
if (len > table->maxlen)
|
len = table->maxlen;
|
len = table->maxlen;
|
memcpy_fromfs(table->data, newval, len);
|
memcpy_fromfs(table->data, newval, len);
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* This function only checks permission for changing the security level
|
* This function only checks permission for changing the security level
|
* If the tests are successful, the actual change is done by
|
* If the tests are successful, the actual change is done by
|
* do_sysctl_strategy
|
* do_sysctl_strategy
|
*/
|
*/
|
static int do_securelevel_strategy (ctl_table *table,
|
static int do_securelevel_strategy (ctl_table *table,
|
int *name, int nlen,
|
int *name, int nlen,
|
void *oldval, size_t *oldlenp,
|
void *oldval, size_t *oldlenp,
|
void *newval, size_t newlen, void **context)
|
void *newval, size_t newlen, void **context)
|
{
|
{
|
int level;
|
int level;
|
|
|
if (newval && newlen) {
|
if (newval && newlen) {
|
if (newlen != sizeof (int))
|
if (newlen != sizeof (int))
|
return -EINVAL;
|
return -EINVAL;
|
memcpy_fromfs (&level, newval, newlen);
|
memcpy_fromfs (&level, newval, newlen);
|
if (level < securelevel && current->pid != 1)
|
if (level < securelevel && current->pid != 1)
|
return -EPERM;
|
return -EPERM;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
struct ctl_table_header *register_sysctl_table(ctl_table * table,
|
struct ctl_table_header *register_sysctl_table(ctl_table * table,
|
int insert_at_head)
|
int insert_at_head)
|
{
|
{
|
struct ctl_table_header *tmp;
|
struct ctl_table_header *tmp;
|
tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
|
tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
|
if (!tmp)
|
if (!tmp)
|
return 0;
|
return 0;
|
*tmp = ((struct ctl_table_header) {table, DNODE_NULL});
|
*tmp = ((struct ctl_table_header) {table, DNODE_NULL});
|
if (insert_at_head)
|
if (insert_at_head)
|
DLIST_INSERT_AFTER(&root_table_header, tmp, ctl_entry);
|
DLIST_INSERT_AFTER(&root_table_header, tmp, ctl_entry);
|
else
|
else
|
DLIST_INSERT_BEFORE(&root_table_header, tmp, ctl_entry);
|
DLIST_INSERT_BEFORE(&root_table_header, tmp, ctl_entry);
|
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
register_proc_table(table, &proc_sys_root);
|
register_proc_table(table, &proc_sys_root);
|
#endif
|
#endif
|
return tmp;
|
return tmp;
|
}
|
}
|
|
|
void unregister_sysctl_table(struct ctl_table_header * header)
|
void unregister_sysctl_table(struct ctl_table_header * header)
|
{
|
{
|
DLIST_DELETE(header, ctl_entry);
|
DLIST_DELETE(header, ctl_entry);
|
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
unregister_proc_table(header->ctl_table, &proc_sys_root);
|
unregister_proc_table(header->ctl_table, &proc_sys_root);
|
#endif
|
#endif
|
kfree(header);
|
kfree(header);
|
}
|
}
|
|
|
/*
|
/*
|
* /proc/sys support
|
* /proc/sys support
|
*/
|
*/
|
|
|
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
|
|
/* Scan the sysctl entries in table and add them all into /proc */
|
/* Scan the sysctl entries in table and add them all into /proc */
|
static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
|
static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
|
{
|
{
|
struct proc_dir_entry *de, *tmp;
|
struct proc_dir_entry *de, *tmp;
|
int exists;
|
int exists;
|
|
|
for (; table->ctl_name; table++) {
|
for (; table->ctl_name; table++) {
|
exists = 0;
|
exists = 0;
|
/* Can't do anything without a proc name. */
|
/* Can't do anything without a proc name. */
|
if (!table->procname)
|
if (!table->procname)
|
continue;
|
continue;
|
/* Maybe we can't do anything with it... */
|
/* Maybe we can't do anything with it... */
|
if (!table->proc_handler &&
|
if (!table->proc_handler &&
|
!table->child)
|
!table->child)
|
continue;
|
continue;
|
|
|
de = kmalloc(sizeof(*de), GFP_KERNEL);
|
de = kmalloc(sizeof(*de), GFP_KERNEL);
|
if (!de) continue;
|
if (!de) continue;
|
de->namelen = strlen(table->procname);
|
de->namelen = strlen(table->procname);
|
de->name = table->procname;
|
de->name = table->procname;
|
de->mode = table->mode;
|
de->mode = table->mode;
|
de->nlink = 1;
|
de->nlink = 1;
|
de->uid = 0;
|
de->uid = 0;
|
de->gid = 0;
|
de->gid = 0;
|
de->size = 0;
|
de->size = 0;
|
de->get_info = 0; /* For internal use if we want it */
|
de->get_info = 0; /* For internal use if we want it */
|
de->fill_inode = 0; /* To override struct inode fields */
|
de->fill_inode = 0; /* To override struct inode fields */
|
de->next = de->subdir = 0;
|
de->next = de->subdir = 0;
|
de->data = (void *) table;
|
de->data = (void *) table;
|
/* Is it a file? */
|
/* Is it a file? */
|
if (table->proc_handler) {
|
if (table->proc_handler) {
|
de->ops = &proc_sys_inode_operations;
|
de->ops = &proc_sys_inode_operations;
|
de->mode |= S_IFREG;
|
de->mode |= S_IFREG;
|
}
|
}
|
/* Otherwise it's a subdir */
|
/* Otherwise it's a subdir */
|
else {
|
else {
|
/* First check to see if it already exists */
|
/* First check to see if it already exists */
|
for (tmp = root->subdir; tmp; tmp = tmp->next) {
|
for (tmp = root->subdir; tmp; tmp = tmp->next) {
|
if (tmp->namelen == de->namelen &&
|
if (tmp->namelen == de->namelen &&
|
!memcmp(tmp->name,de->name,de->namelen)) {
|
!memcmp(tmp->name,de->name,de->namelen)) {
|
exists = 1;
|
exists = 1;
|
kfree (de);
|
kfree (de);
|
de = tmp;
|
de = tmp;
|
}
|
}
|
}
|
}
|
if (!exists) {
|
if (!exists) {
|
de->ops = &proc_dir_inode_operations;
|
de->ops = &proc_dir_inode_operations;
|
de->nlink++;
|
de->nlink++;
|
de->mode |= S_IFDIR;
|
de->mode |= S_IFDIR;
|
}
|
}
|
}
|
}
|
table->de = de;
|
table->de = de;
|
if (!exists)
|
if (!exists)
|
proc_register_dynamic(root, de);
|
proc_register_dynamic(root, de);
|
if (de->mode & S_IFDIR )
|
if (de->mode & S_IFDIR )
|
register_proc_table(table->child, de);
|
register_proc_table(table->child, de);
|
}
|
}
|
}
|
}
|
|
|
static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
|
static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
|
{
|
{
|
struct proc_dir_entry *de;
|
struct proc_dir_entry *de;
|
for (; table->ctl_name; table++) {
|
for (; table->ctl_name; table++) {
|
if (!(de = table->de))
|
if (!(de = table->de))
|
continue;
|
continue;
|
if (de->mode & S_IFDIR) {
|
if (de->mode & S_IFDIR) {
|
if (!table->child) {
|
if (!table->child) {
|
printk (KERN_ALERT "Help - malformed sysctl tree on free\n");
|
printk (KERN_ALERT "Help - malformed sysctl tree on free\n");
|
continue;
|
continue;
|
}
|
}
|
unregister_proc_table(table->child, de);
|
unregister_proc_table(table->child, de);
|
}
|
}
|
/* Don't unregister proc directories which still have
|
/* Don't unregister proc directories which still have
|
entries... */
|
entries... */
|
if (!((de->mode & S_IFDIR) && de->subdir)) {
|
if (!((de->mode & S_IFDIR) && de->subdir)) {
|
proc_unregister(root, de->low_ino);
|
proc_unregister(root, de->low_ino);
|
table->de = NULL;
|
table->de = NULL;
|
kfree(de);
|
kfree(de);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
|
|
static int do_rw_proc(int write, struct inode * inode, struct file * file,
|
static int do_rw_proc(int write, struct inode * inode, struct file * file,
|
char * buf, int count)
|
char * buf, int count)
|
{
|
{
|
int error, op;
|
int error, op;
|
struct proc_dir_entry *de;
|
struct proc_dir_entry *de;
|
struct ctl_table *table;
|
struct ctl_table *table;
|
size_t res;
|
size_t res;
|
|
|
error = verify_area(write ? VERIFY_READ : VERIFY_WRITE, buf, count);
|
error = verify_area(write ? VERIFY_READ : VERIFY_WRITE, buf, count);
|
if (error)
|
if (error)
|
return error;
|
return error;
|
|
|
de = (struct proc_dir_entry*) inode->u.generic_ip;
|
de = (struct proc_dir_entry*) inode->u.generic_ip;
|
if (!de || !de->data)
|
if (!de || !de->data)
|
return -ENOTDIR;
|
return -ENOTDIR;
|
table = (struct ctl_table *) de->data;
|
table = (struct ctl_table *) de->data;
|
if (!table || !table->proc_handler)
|
if (!table || !table->proc_handler)
|
return -ENOTDIR;
|
return -ENOTDIR;
|
op = (write ? 002 : 004);
|
op = (write ? 002 : 004);
|
if (ctl_perm(table, op))
|
if (ctl_perm(table, op))
|
return -EPERM;
|
return -EPERM;
|
|
|
res = count;
|
res = count;
|
error = (*table->proc_handler) (table, write, file, buf, &res);
|
error = (*table->proc_handler) (table, write, file, buf, &res);
|
if (error)
|
if (error)
|
return error;
|
return error;
|
return res;
|
return res;
|
}
|
}
|
|
|
static int proc_readsys(struct inode * inode, struct file * file,
|
static int proc_readsys(struct inode * inode, struct file * file,
|
char * buf, int count)
|
char * buf, int count)
|
{
|
{
|
return do_rw_proc(0, inode, file, buf, count);
|
return do_rw_proc(0, inode, file, buf, count);
|
}
|
}
|
|
|
static int proc_writesys(struct inode * inode, struct file * file,
|
static int proc_writesys(struct inode * inode, struct file * file,
|
const char * buf, int count)
|
const char * buf, int count)
|
{
|
{
|
return do_rw_proc(1, inode, file, (char *) buf, count);
|
return do_rw_proc(1, inode, file, (char *) buf, count);
|
}
|
}
|
|
|
static int proc_sys_permission(struct inode *inode, int op)
|
static int proc_sys_permission(struct inode *inode, int op)
|
{
|
{
|
return test_perm(inode->i_mode, op);
|
return test_perm(inode->i_mode, op);
|
}
|
}
|
|
|
int proc_dostring(ctl_table *table, int write, struct file *filp,
|
int proc_dostring(ctl_table *table, int write, struct file *filp,
|
void *buffer, size_t *lenp)
|
void *buffer, size_t *lenp)
|
{
|
{
|
int len;
|
int len;
|
char *p, c;
|
char *p, c;
|
|
|
if (!table->data || !table->maxlen || !*lenp ||
|
if (!table->data || !table->maxlen || !*lenp ||
|
(filp->f_pos && !write)) {
|
(filp->f_pos && !write)) {
|
*lenp = 0;
|
*lenp = 0;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if (write) {
|
if (write) {
|
len = 0;
|
len = 0;
|
p = buffer;
|
p = buffer;
|
while (len < *lenp &&
|
while (len < *lenp &&
|
(c = get_user(p++)) != 0 && c != '\n')
|
(c = get_user(p++)) != 0 && c != '\n')
|
len++;
|
len++;
|
if (len >= table->maxlen)
|
if (len >= table->maxlen)
|
len = table->maxlen-1;
|
len = table->maxlen-1;
|
memcpy_fromfs(table->data, buffer, len);
|
memcpy_fromfs(table->data, buffer, len);
|
((char *) table->data)[len] = 0;
|
((char *) table->data)[len] = 0;
|
filp->f_pos += *lenp;
|
filp->f_pos += *lenp;
|
} else {
|
} else {
|
len = strlen(table->data);
|
len = strlen(table->data);
|
if (len > table->maxlen)
|
if (len > table->maxlen)
|
len = table->maxlen;
|
len = table->maxlen;
|
if (len > *lenp)
|
if (len > *lenp)
|
len = *lenp;
|
len = *lenp;
|
if (len)
|
if (len)
|
memcpy_tofs(buffer, table->data, len);
|
memcpy_tofs(buffer, table->data, len);
|
if (len < *lenp) {
|
if (len < *lenp) {
|
put_user('\n', ((char *) buffer) + len);
|
put_user('\n', ((char *) buffer) + len);
|
len++;
|
len++;
|
}
|
}
|
*lenp = len;
|
*lenp = len;
|
filp->f_pos += len;
|
filp->f_pos += len;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int proc_dointvec(ctl_table *table, int write, struct file *filp,
|
int proc_dointvec(ctl_table *table, int write, struct file *filp,
|
void *buffer, size_t *lenp)
|
void *buffer, size_t *lenp)
|
{
|
{
|
int *i, vleft, first=1, len, left, neg, val;
|
int *i, vleft, first=1, len, left, neg, val;
|
#define TMPBUFLEN 20
|
#define TMPBUFLEN 20
|
char buf[TMPBUFLEN], *p;
|
char buf[TMPBUFLEN], *p;
|
|
|
if (!table->data || !table->maxlen || !*lenp ||
|
if (!table->data || !table->maxlen || !*lenp ||
|
(filp->f_pos && !write)) {
|
(filp->f_pos && !write)) {
|
*lenp = 0;
|
*lenp = 0;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
i = (int *) table->data;
|
i = (int *) table->data;
|
vleft = table->maxlen / sizeof(int);
|
vleft = table->maxlen / sizeof(int);
|
left = *lenp;
|
left = *lenp;
|
|
|
for (; left && vleft--; i++, first=0) {
|
for (; left && vleft--; i++, first=0) {
|
if (write) {
|
if (write) {
|
while (left && isspace(get_user((char *) buffer)))
|
while (left && isspace(get_user((char *) buffer)))
|
left--, ((char *) buffer)++;
|
left--, ((char *) buffer)++;
|
if (!left)
|
if (!left)
|
break;
|
break;
|
neg = 0;
|
neg = 0;
|
len = left;
|
len = left;
|
if (len > TMPBUFLEN-1)
|
if (len > TMPBUFLEN-1)
|
len = TMPBUFLEN-1;
|
len = TMPBUFLEN-1;
|
memcpy_fromfs(buf, buffer, len);
|
memcpy_fromfs(buf, buffer, len);
|
buf[len] = 0;
|
buf[len] = 0;
|
p = buf;
|
p = buf;
|
if (*p == '-' && left > 1) {
|
if (*p == '-' && left > 1) {
|
neg = 1;
|
neg = 1;
|
left--, p++;
|
left--, p++;
|
}
|
}
|
if (*p < '0' || *p > '9')
|
if (*p < '0' || *p > '9')
|
break;
|
break;
|
val = simple_strtoul(p, &p, 0);
|
val = simple_strtoul(p, &p, 0);
|
len = p-buf;
|
len = p-buf;
|
if ((len < left) && *p && !isspace(*p))
|
if ((len < left) && *p && !isspace(*p))
|
break;
|
break;
|
if (neg)
|
if (neg)
|
val = -val;
|
val = -val;
|
buffer += len;
|
buffer += len;
|
left -= len;
|
left -= len;
|
*i = val;
|
*i = val;
|
} else {
|
} else {
|
p = buf;
|
p = buf;
|
if (!first)
|
if (!first)
|
*p++ = '\t';
|
*p++ = '\t';
|
sprintf(p, "%d", *i);
|
sprintf(p, "%d", *i);
|
len = strlen(buf);
|
len = strlen(buf);
|
if (len > left)
|
if (len > left)
|
len = left;
|
len = left;
|
memcpy_tofs(buffer, buf, len);
|
memcpy_tofs(buffer, buf, len);
|
left -= len;
|
left -= len;
|
buffer += len;
|
buffer += len;
|
}
|
}
|
}
|
}
|
|
|
if (!write && !first && left) {
|
if (!write && !first && left) {
|
put_user('\n', (char *) buffer);
|
put_user('\n', (char *) buffer);
|
left--, buffer++;
|
left--, buffer++;
|
}
|
}
|
if (write) {
|
if (write) {
|
p = (char *) buffer;
|
p = (char *) buffer;
|
while (left && isspace(get_user(p++)))
|
while (left && isspace(get_user(p++)))
|
left--;
|
left--;
|
}
|
}
|
if (write && first)
|
if (write && first)
|
return -EINVAL;
|
return -EINVAL;
|
*lenp -= left;
|
*lenp -= left;
|
filp->f_pos += *lenp;
|
filp->f_pos += *lenp;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
|
int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
|
void *buffer, size_t *lenp)
|
void *buffer, size_t *lenp)
|
{
|
{
|
int *i, *min, *max, vleft, first=1, len, left, neg, val;
|
int *i, *min, *max, vleft, first=1, len, left, neg, val;
|
#define TMPBUFLEN 20
|
#define TMPBUFLEN 20
|
char buf[TMPBUFLEN], *p;
|
char buf[TMPBUFLEN], *p;
|
|
|
if (!table->data || !table->maxlen || !*lenp ||
|
if (!table->data || !table->maxlen || !*lenp ||
|
(filp->f_pos && !write)) {
|
(filp->f_pos && !write)) {
|
*lenp = 0;
|
*lenp = 0;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
i = (int *) table->data;
|
i = (int *) table->data;
|
min = (int *) table->extra1;
|
min = (int *) table->extra1;
|
max = (int *) table->extra2;
|
max = (int *) table->extra2;
|
vleft = table->maxlen / sizeof(int);
|
vleft = table->maxlen / sizeof(int);
|
left = *lenp;
|
left = *lenp;
|
|
|
for (; left && vleft--; i++, first=0) {
|
for (; left && vleft--; i++, first=0) {
|
if (write) {
|
if (write) {
|
while (left && isspace(get_user((char *) buffer)))
|
while (left && isspace(get_user((char *) buffer)))
|
left--, ((char *) buffer)++;
|
left--, ((char *) buffer)++;
|
if (!left)
|
if (!left)
|
break;
|
break;
|
neg = 0;
|
neg = 0;
|
len = left;
|
len = left;
|
if (len > TMPBUFLEN-1)
|
if (len > TMPBUFLEN-1)
|
len = TMPBUFLEN-1;
|
len = TMPBUFLEN-1;
|
memcpy_fromfs(buf, buffer, len);
|
memcpy_fromfs(buf, buffer, len);
|
buf[len] = 0;
|
buf[len] = 0;
|
p = buf;
|
p = buf;
|
if (*p == '-' && left > 1) {
|
if (*p == '-' && left > 1) {
|
neg = 1;
|
neg = 1;
|
left--, p++;
|
left--, p++;
|
}
|
}
|
if (*p < '0' || *p > '9')
|
if (*p < '0' || *p > '9')
|
break;
|
break;
|
val = simple_strtoul(p, &p, 0);
|
val = simple_strtoul(p, &p, 0);
|
len = p-buf;
|
len = p-buf;
|
if ((len < left) && *p && !isspace(*p))
|
if ((len < left) && *p && !isspace(*p))
|
break;
|
break;
|
if (neg)
|
if (neg)
|
val = -val;
|
val = -val;
|
buffer += len;
|
buffer += len;
|
left -= len;
|
left -= len;
|
|
|
if (min && val < *min++)
|
if (min && val < *min++)
|
continue;
|
continue;
|
if (max && val > *max++)
|
if (max && val > *max++)
|
continue;
|
continue;
|
*i = val;
|
*i = val;
|
} else {
|
} else {
|
p = buf;
|
p = buf;
|
if (!first)
|
if (!first)
|
*p++ = '\t';
|
*p++ = '\t';
|
sprintf(p, "%d", *i);
|
sprintf(p, "%d", *i);
|
len = strlen(buf);
|
len = strlen(buf);
|
if (len > left)
|
if (len > left)
|
len = left;
|
len = left;
|
memcpy_tofs(buffer, buf, len);
|
memcpy_tofs(buffer, buf, len);
|
left -= len;
|
left -= len;
|
buffer += len;
|
buffer += len;
|
}
|
}
|
}
|
}
|
|
|
if (!write && !first && left) {
|
if (!write && !first && left) {
|
put_user('\n', (char *) buffer);
|
put_user('\n', (char *) buffer);
|
left--, buffer++;
|
left--, buffer++;
|
}
|
}
|
if (write) {
|
if (write) {
|
p = (char *) buffer;
|
p = (char *) buffer;
|
while (left && isspace(get_user(p++)))
|
while (left && isspace(get_user(p++)))
|
left--;
|
left--;
|
}
|
}
|
if (write && first)
|
if (write && first)
|
return -EINVAL;
|
return -EINVAL;
|
*lenp -= left;
|
*lenp -= left;
|
filp->f_pos += *lenp;
|
filp->f_pos += *lenp;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
#else /* CONFIG_PROC_FS */
|
#else /* CONFIG_PROC_FS */
|
|
|
int proc_dostring(ctl_table *table, int write, struct file *filp,
|
int proc_dostring(ctl_table *table, int write, struct file *filp,
|
void *buffer, size_t *lenp)
|
void *buffer, size_t *lenp)
|
{
|
{
|
return -ENOSYS;
|
return -ENOSYS;
|
}
|
}
|
|
|
int proc_dointvec(ctl_table *table, int write, struct file *filp,
|
int proc_dointvec(ctl_table *table, int write, struct file *filp,
|
void *buffer, size_t *lenp)
|
void *buffer, size_t *lenp)
|
{
|
{
|
return -ENOSYS;
|
return -ENOSYS;
|
}
|
}
|
|
|
int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
|
int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
|
void *buffer, size_t *lenp)
|
void *buffer, size_t *lenp)
|
{
|
{
|
return -ENOSYS;
|
return -ENOSYS;
|
}
|
}
|
|
|
#endif /* CONFIG_PROC_FS */
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
|
/*
|
/*
|
* General sysctl support routines
|
* General sysctl support routines
|
*/
|
*/
|
|
|
/* The generic string strategy routine: */
|
/* The generic string strategy routine: */
|
int sysctl_string(ctl_table *table, int *name, int nlen,
|
int sysctl_string(ctl_table *table, int *name, int nlen,
|
void *oldval, size_t *oldlenp,
|
void *oldval, size_t *oldlenp,
|
void *newval, size_t newlen, void **context)
|
void *newval, size_t newlen, void **context)
|
{
|
{
|
int l, len;
|
int l, len;
|
|
|
if (!table->data || !table->maxlen)
|
if (!table->data || !table->maxlen)
|
return -ENOTDIR;
|
return -ENOTDIR;
|
|
|
if (oldval && oldlenp && get_user(oldlenp)) {
|
if (oldval && oldlenp && get_user(oldlenp)) {
|
len = get_user(oldlenp);
|
len = get_user(oldlenp);
|
l = strlen(table->data);
|
l = strlen(table->data);
|
if (len > l) len = l;
|
if (len > l) len = l;
|
if (len >= table->maxlen)
|
if (len >= table->maxlen)
|
len = table->maxlen;
|
len = table->maxlen;
|
memcpy_tofs(oldval, table->data, len);
|
memcpy_tofs(oldval, table->data, len);
|
put_user(0, ((char *) oldval) + len);
|
put_user(0, ((char *) oldval) + len);
|
put_user(len, oldlenp);
|
put_user(len, oldlenp);
|
}
|
}
|
if (newval && newlen) {
|
if (newval && newlen) {
|
len = newlen;
|
len = newlen;
|
if (len > table->maxlen)
|
if (len > table->maxlen)
|
len = table->maxlen;
|
len = table->maxlen;
|
memcpy_fromfs(table->data, newval, len);
|
memcpy_fromfs(table->data, newval, len);
|
if (len == table->maxlen)
|
if (len == table->maxlen)
|
len--;
|
len--;
|
((char *) table->data)[len] = 0;
|
((char *) table->data)[len] = 0;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* This function makes sure that all of the integers in the vector
|
* This function makes sure that all of the integers in the vector
|
* are between the minimum and maximum values given in the arrays
|
* are between the minimum and maximum values given in the arrays
|
* table->extra1 and table->extra2, respectively.
|
* table->extra1 and table->extra2, respectively.
|
*/
|
*/
|
int sysctl_intvec(ctl_table *table, int *name, int nlen,
|
int sysctl_intvec(ctl_table *table, int *name, int nlen,
|
void *oldval, size_t *oldlenp,
|
void *oldval, size_t *oldlenp,
|
void *newval, size_t newlen, void **context)
|
void *newval, size_t newlen, void **context)
|
{
|
{
|
int i, length, *vec, *min, *max;
|
int i, length, *vec, *min, *max;
|
|
|
if (newval && newlen) {
|
if (newval && newlen) {
|
if (newlen % sizeof(int) != 0)
|
if (newlen % sizeof(int) != 0)
|
return -EINVAL;
|
return -EINVAL;
|
|
|
if (!table->extra1 && !table->extra2)
|
if (!table->extra1 && !table->extra2)
|
return 0;
|
return 0;
|
|
|
if (newlen > table->maxlen)
|
if (newlen > table->maxlen)
|
newlen = table->maxlen;
|
newlen = table->maxlen;
|
length = newlen / sizeof(int);
|
length = newlen / sizeof(int);
|
|
|
vec = (int *) newval;
|
vec = (int *) newval;
|
min = (int *) table->extra1;
|
min = (int *) table->extra1;
|
max = (int *) table->extra2;
|
max = (int *) table->extra2;
|
|
|
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
int value = get_user(vec + i);
|
int value = get_user(vec + i);
|
if (min && value < min[i])
|
if (min && value < min[i])
|
return -EINVAL;
|
return -EINVAL;
|
if (max && value > max[i])
|
if (max && value > max[i])
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int do_string (
|
int do_string (
|
void *oldval, size_t *oldlenp, void *newval, size_t newlen,
|
void *oldval, size_t *oldlenp, void *newval, size_t newlen,
|
int rdwr, char *data, size_t max)
|
int rdwr, char *data, size_t max)
|
{
|
{
|
int l = strlen(data) + 1;
|
int l = strlen(data) + 1;
|
if (newval && !rdwr)
|
if (newval && !rdwr)
|
return -EPERM;
|
return -EPERM;
|
if (newval && newlen >= max)
|
if (newval && newlen >= max)
|
return -EINVAL;
|
return -EINVAL;
|
if (oldval) {
|
if (oldval) {
|
if (l > get_user(oldlenp))
|
if (l > get_user(oldlenp))
|
return -ENOMEM;
|
return -ENOMEM;
|
put_user(l, oldlenp);
|
put_user(l, oldlenp);
|
memcpy_tofs(oldval, data, l);
|
memcpy_tofs(oldval, data, l);
|
}
|
}
|
if (newval) {
|
if (newval) {
|
memcpy_fromfs(data, newval, newlen);
|
memcpy_fromfs(data, newval, newlen);
|
data[newlen] = 0;
|
data[newlen] = 0;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int do_int (
|
int do_int (
|
void *oldval, size_t *oldlenp, void *newval, size_t newlen,
|
void *oldval, size_t *oldlenp, void *newval, size_t newlen,
|
int rdwr, int *data)
|
int rdwr, int *data)
|
{
|
{
|
if (newval && !rdwr)
|
if (newval && !rdwr)
|
return -EPERM;
|
return -EPERM;
|
if (newval && newlen != sizeof(int))
|
if (newval && newlen != sizeof(int))
|
return -EINVAL;
|
return -EINVAL;
|
if (oldval) {
|
if (oldval) {
|
if (get_user(oldlenp) < sizeof(int))
|
if (get_user(oldlenp) < sizeof(int))
|
return -ENOMEM;
|
return -ENOMEM;
|
put_user(sizeof(int), oldlenp);
|
put_user(sizeof(int), oldlenp);
|
memcpy_tofs(oldval, data, sizeof(int));
|
memcpy_tofs(oldval, data, sizeof(int));
|
}
|
}
|
if (newval)
|
if (newval)
|
memcpy_fromfs(data, newval, sizeof(int));
|
memcpy_fromfs(data, newval, sizeof(int));
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int do_struct (
|
int do_struct (
|
void *oldval, size_t *oldlenp, void *newval, size_t newlen,
|
void *oldval, size_t *oldlenp, void *newval, size_t newlen,
|
int rdwr, void *data, size_t len)
|
int rdwr, void *data, size_t len)
|
{
|
{
|
if (newval && !rdwr)
|
if (newval && !rdwr)
|
return -EPERM;
|
return -EPERM;
|
if (newval && newlen != len)
|
if (newval && newlen != len)
|
return -EINVAL;
|
return -EINVAL;
|
if (oldval) {
|
if (oldval) {
|
if (get_user(oldlenp) < len)
|
if (get_user(oldlenp) < len)
|
return -ENOMEM;
|
return -ENOMEM;
|
put_user(len, oldlenp);
|
put_user(len, oldlenp);
|
memcpy_tofs(oldval, data, len);
|
memcpy_tofs(oldval, data, len);
|
}
|
}
|
if (newval)
|
if (newval)
|
memcpy_fromfs(data, newval, len);
|
memcpy_fromfs(data, newval, len);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|