#include <linux/sysctl.h>
#include <linux/bitmap.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/highuid.h>
#include <linux/writeback.h>
#include <linux/initrd.h>
#include <linux/limits.h>
#include <linux/syscalls.h>
#include <linux/capability.h>
#include "../lib/kstrtox.h"
#include <linux/uaccess.h>
#include <asm/processor.h>
const int sysctl_vals[] = { 0, 1, 2, 3, 4, 100, 200, 1000, 3000, INT_MAX, 65535, -1 };
EXPORT_SYMBOL(sysctl_vals);
const unsigned long sysctl_long_vals[] = { 0, 1, LONG_MAX };
EXPORT_SYMBOL_GPL(sysctl_long_vals);
#if defined(CONFIG_SYSCTL)
static const int ngroups_max = NGROUPS_MAX;
static const int cap_last_cap = CAP_LAST_CAP;
#ifdef CONFIG_PROC_SYSCTL
enum sysctl_writes_mode {
SYSCTL_WRITES_LEGACY = -1,
SYSCTL_WRITES_WARN = 0,
SYSCTL_WRITES_STRICT = 1,
};
static enum sysctl_writes_mode sysctl_writes_strict = SYSCTL_WRITES_STRICT;
#endif
#endif
#ifdef CONFIG_PROC_SYSCTL
static int _proc_do_string(char *data, int maxlen, int dir,
char *buffer, size_t *lenp, loff_t *ppos)
{
size_t len;
char c, *p;
if (!data || !maxlen || !*lenp) {
*lenp = 0;
return 0;
}
if (SYSCTL_USER_TO_KERN(dir)) {
if (sysctl_writes_strict == SYSCTL_WRITES_STRICT) {
len = strlen(data);
if (len > maxlen - 1)
len = maxlen - 1;
if (*ppos > len)
return 0;
len = *ppos;
} else {
len = 0;
}
*ppos += *lenp;
p = buffer;
while ((p - buffer) < *lenp && len < maxlen - 1) {
c = *(p++);
if (c == 0 || c == '\n')
break;
data[len++] = c;
}
data[len] = 0;
} else {
len = strlen(data);
if (len > maxlen)
len = maxlen;
if (*ppos > len) {
*lenp = 0;
return 0;
}
data += *ppos;
len -= *ppos;
if (len > *lenp)
len = *lenp;
if (len)
memcpy(buffer, data, len);
if (len < *lenp) {
buffer[len] = '\n';
len++;
}
*lenp = len;
*ppos += len;
}
return 0;
}
static void warn_sysctl_write(const struct ctl_table *table)
{
pr_warn_once("%s wrote to %s when file position was not 0!\n"
"This will not be supported in the future. To silence this\n"
"warning, set kernel.sysctl_writes_strict = -1\n",
current->comm, table->procname);
}
static bool proc_first_pos_non_zero_ignore(loff_t *ppos,
const struct ctl_table *table)
{
if (!*ppos)
return false;
switch (sysctl_writes_strict) {
case SYSCTL_WRITES_STRICT:
return true;
case SYSCTL_WRITES_WARN:
warn_sysctl_write(table);
return false;
default:
return false;
}
}
int proc_dostring(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
if (SYSCTL_USER_TO_KERN(dir))
proc_first_pos_non_zero_ignore(ppos, table);
return _proc_do_string(table->data, table->maxlen, dir, buffer, lenp,
ppos);
}
static void proc_skip_spaces(char **buf, size_t *size)
{
while (*size) {
if (!isspace(**buf))
break;
(*size)--;
(*buf)++;
}
}
static void proc_skip_char(char **buf, size_t *size, const char v)
{
while (*size) {
if (**buf != v)
break;
(*size)--;
(*buf)++;
}
}
static int strtoul_lenient(const char *cp, char **endp, unsigned int base,
unsigned long *res)
{
unsigned long long result;
unsigned int rv;
cp = _parse_integer_fixup_radix(cp, &base);
rv = _parse_integer(cp, base, &result);
if ((rv & KSTRTOX_OVERFLOW) || (result != (unsigned long)result))
return -ERANGE;
cp += rv;
if (endp)
*endp = (char *)cp;
*res = (unsigned long)result;
return 0;
}
#define TMPBUFLEN 22
static int proc_get_long(char **buf, size_t *size,
unsigned long *val, bool *neg,
const char *perm_tr, unsigned perm_tr_len, char *tr)
{
char *p, tmp[TMPBUFLEN];
ssize_t len = *size;
if (len <= 0)
return -EINVAL;
if (len > TMPBUFLEN - 1)
len = TMPBUFLEN - 1;
memcpy(tmp, *buf, len);
tmp[len] = 0;
p = tmp;
if (*p == '-' && *size > 1) {
*neg = true;
p++;
} else
*neg = false;
if (!isdigit(*p))
return -EINVAL;
if (strtoul_lenient(p, &p, 0, val))
return -EINVAL;
len = p - tmp;
if (len == TMPBUFLEN - 1)
return -EINVAL;
if (len < *size && perm_tr_len && !memchr(perm_tr, *p, perm_tr_len))
return -EINVAL;
if (tr && (len < *size))
*tr = *p;
*buf += len;
*size -= len;
return 0;
}
static void proc_put_long(void **buf, size_t *size, unsigned long val, bool neg)
{
int len;
char tmp[TMPBUFLEN], *p = tmp;
sprintf(p, "%s%lu", neg ? "-" : "", val);
len = strlen(tmp);
if (len > *size)
len = *size;
memcpy(*buf, tmp, len);
*size -= len;
*buf += len;
}
#undef TMPBUFLEN
static void proc_put_char(void **buf, size_t *size, char c)
{
if (*size) {
char **buffer = (char **)buf;
**buffer = c;
(*size)--;
(*buffer)++;
*buf = *buffer;
}
}
int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr,
ulong (*u_ptr_op)(const ulong))
{
ulong u = u_ptr_op ? u_ptr_op(*u_ptr) : *u_ptr;
if (u > UINT_MAX)
return -EINVAL;
WRITE_ONCE(*k_ptr, u);
return 0;
}
int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr)
{
uint val = READ_ONCE(*k_ptr);
*u_ptr = (ulong)val;
return 0;
}
int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr),
int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr))
{
if (SYSCTL_KERN_TO_USER(dir))
return kern_to_user(u_ptr, k_ptr);
if (k_ptr_range_check) {
uint tmp_k;
int ret;
if (!tbl)
return -EINVAL;
ret = user_to_kern(u_ptr, &tmp_k);
if (ret)
return ret;
if ((tbl->extra1 &&
*(uint *)tbl->extra1 > tmp_k) ||
(tbl->extra2 &&
*(uint *)tbl->extra2 < tmp_k))
return -ERANGE;
WRITE_ONCE(*k_ptr, tmp_k);
} else
return user_to_kern(u_ptr, k_ptr);
return 0;
}
static int proc_uint_u2k_conv(const ulong *u_ptr, uint *k_ptr)
{
return proc_uint_u2k_conv_uop(u_ptr, k_ptr, NULL);
}
static int do_proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl)
{
return proc_uint_conv(u_ptr, k_ptr, dir, tbl, false,
proc_uint_u2k_conv, proc_uint_k2u_conv);
}
static int do_proc_uint_conv_minmax(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl)
{
return proc_uint_conv(u_ptr, k_ptr, dir, tbl, true,
proc_uint_u2k_conv, proc_uint_k2u_conv);
}
int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp,
ulong (*k_ptr_op)(const ulong))
{
int val = READ_ONCE(*k_ptr);
if (val < 0) {
*negp = true;
*u_ptr = k_ptr_op ? -k_ptr_op((ulong)val) : -(ulong)val;
} else {
*negp = false;
*u_ptr = k_ptr_op ? k_ptr_op((ulong)val) : (ulong) val;
}
return 0;
}
int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp,
ulong (*u_ptr_op)(const ulong))
{
ulong u = u_ptr_op ? u_ptr_op(*u_ptr) : *u_ptr;
if (*negp) {
if (u > (ulong) INT_MAX + 1)
return -EINVAL;
WRITE_ONCE(*k_ptr, -u);
} else {
if (u > (ulong) INT_MAX)
return -EINVAL;
WRITE_ONCE(*k_ptr, u);
}
return 0;
}
int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr),
int (*kern_to_user)(bool *negp, ulong *u_ptr, const int *k_ptr))
{
if (SYSCTL_KERN_TO_USER(dir))
return kern_to_user(negp, u_ptr, k_ptr);
if (k_ptr_range_check) {
int tmp_k, ret;
if (!tbl)
return -EINVAL;
ret = user_to_kern(negp, u_ptr, &tmp_k);
if (ret)
return ret;
if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) ||
(tbl->extra2 && *(int *)tbl->extra2 < tmp_k))
return -EINVAL;
WRITE_ONCE(*k_ptr, tmp_k);
} else
return user_to_kern(negp, u_ptr, k_ptr);
return 0;
}
static int sysctl_user_to_kern_int_conv(const bool *negp, const ulong *u_ptr,
int *k_ptr)
{
return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, NULL);
}
static int sysctl_kern_to_user_int_conv(bool *negp, ulong *u_ptr, const int *k_ptr)
{
return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, NULL);
}
static int do_proc_int_conv(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_user_to_kern_int_conv,
sysctl_kern_to_user_int_conv);
}
static int do_proc_int_conv_minmax(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true,
sysctl_user_to_kern_int_conv,
sysctl_kern_to_user_int_conv);
}
static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
static int do_proc_dointvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *table))
{
int *i, vleft, first = 1, err = 0;
size_t left;
char *p;
if (!table->data || !table->maxlen || !*lenp ||
(*ppos && SYSCTL_KERN_TO_USER(dir))) {
*lenp = 0;
return 0;
}
i = (int *) table->data;
vleft = table->maxlen / sizeof(*i);
left = *lenp;
if (!conv)
conv = do_proc_int_conv;
if (SYSCTL_USER_TO_KERN(dir)) {
if (proc_first_pos_non_zero_ignore(ppos, table))
goto out;
if (left > PAGE_SIZE - 1)
left = PAGE_SIZE - 1;
p = buffer;
}
for (; left && vleft--; i++, first=0) {
unsigned long lval;
bool neg;
if (SYSCTL_USER_TO_KERN(dir)) {
proc_skip_spaces(&p, &left);
if (!left)
break;
err = proc_get_long(&p, &left, &lval, &neg,
proc_wspace_sep,
sizeof(proc_wspace_sep), NULL);
if (err)
break;
if (conv(&neg, &lval, i, 1, table)) {
err = -EINVAL;
break;
}
} else {
if (conv(&neg, &lval, i, 0, table)) {
err = -EINVAL;
break;
}
if (!first)
proc_put_char(&buffer, &left, '\t');
proc_put_long(&buffer, &left, lval, neg);
}
}
if (SYSCTL_KERN_TO_USER(dir) && !first && left && !err)
proc_put_char(&buffer, &left, '\n');
if (SYSCTL_USER_TO_KERN(dir) && !err && left)
proc_skip_spaces(&p, &left);
if (SYSCTL_USER_TO_KERN(dir) && first)
return err ? : -EINVAL;
*lenp -= left;
out:
*ppos += *lenp;
return err;
}
static int do_proc_douintvec_w(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *u_ptr,
unsigned int *k_ptr, int dir,
const struct ctl_table *table))
{
unsigned long lval;
int err = 0;
size_t left;
bool neg;
char *p = buffer;
left = *lenp;
if (proc_first_pos_non_zero_ignore(ppos, table))
goto bail_early;
if (left > PAGE_SIZE - 1)
left = PAGE_SIZE - 1;
proc_skip_spaces(&p, &left);
if (!left) {
err = -EINVAL;
goto out_free;
}
err = proc_get_long(&p, &left, &lval, &neg,
proc_wspace_sep,
sizeof(proc_wspace_sep), NULL);
if (err || neg) {
err = -EINVAL;
goto out_free;
}
if (conv(&lval, (unsigned int *) table->data, 1, table)) {
err = -EINVAL;
goto out_free;
}
if (!err && left)
proc_skip_spaces(&p, &left);
out_free:
if (err)
return -EINVAL;
return 0;
bail_early:
*ppos += *lenp;
return err;
}
static int do_proc_douintvec_r(const struct ctl_table *table, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *u_ptr,
unsigned int *k_ptr, int dir,
const struct ctl_table *table))
{
unsigned long lval;
int err = 0;
size_t left;
left = *lenp;
if (conv(&lval, (unsigned int *) table->data, 0, table)) {
err = -EINVAL;
goto out;
}
proc_put_long(&buffer, &left, lval, false);
if (!left)
goto out;
proc_put_char(&buffer, &left, '\n');
out:
*lenp -= left;
*ppos += *lenp;
return err;
}
static int do_proc_douintvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *u_ptr,
unsigned int *k_ptr, int dir,
const struct ctl_table *table))
{
unsigned int vleft;
if (!table->data || !table->maxlen || !*lenp ||
(*ppos && SYSCTL_KERN_TO_USER(dir))) {
*lenp = 0;
return 0;
}
vleft = table->maxlen / sizeof(unsigned int);
if (vleft != 1) {
*lenp = 0;
return -EINVAL;
}
if (!conv)
conv = do_proc_uint_conv;
if (SYSCTL_USER_TO_KERN(dir))
return do_proc_douintvec_w(table, buffer, lenp, ppos, conv);
return do_proc_douintvec_r(table, buffer, lenp, ppos, conv);
}
int proc_douintvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *u_ptr, unsigned int *k_ptr,
int dir, const struct ctl_table *table))
{
return do_proc_douintvec(table, dir, buffer, lenp, ppos, conv);
}
int proc_dobool(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos)
{
struct ctl_table tmp;
bool *data = table->data;
int res, val;
if (table->maxlen != sizeof(bool))
return -EINVAL;
tmp = *table;
tmp.maxlen = sizeof(val);
tmp.data = &val;
val = READ_ONCE(*data);
res = proc_dointvec(&tmp, dir, buffer, lenp, ppos);
if (res)
return res;
if (SYSCTL_USER_TO_KERN(dir))
WRITE_ONCE(*data, val);
return 0;
}
int proc_dointvec(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, dir, buffer, lenp, ppos, NULL);
}
int proc_douintvec(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos)
{
return do_proc_douintvec(table, dir, buffer, lenp, ppos,
do_proc_uint_conv);
}
int proc_dointvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_dointvec(table, dir, buffer, lenp, ppos,
do_proc_int_conv_minmax);
}
int proc_douintvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return do_proc_douintvec(table, dir, buffer, lenp, ppos,
do_proc_uint_conv_minmax);
}
int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct ctl_table tmp;
unsigned int min = 0, max = 255U, val;
u8 *data = table->data;
int res;
if (table->maxlen != sizeof(u8))
return -EINVAL;
tmp = *table;
tmp.maxlen = sizeof(val);
tmp.data = &val;
if (!tmp.extra1)
tmp.extra1 = (unsigned int *) &min;
if (!tmp.extra2)
tmp.extra2 = (unsigned int *) &max;
val = READ_ONCE(*data);
res = do_proc_douintvec(&tmp, dir, buffer, lenp, ppos,
do_proc_uint_conv_minmax);
if (res)
return res;
if (SYSCTL_USER_TO_KERN(dir))
WRITE_ONCE(*data, val);
return 0;
}
EXPORT_SYMBOL_GPL(proc_dou8vec_minmax);
static int do_proc_doulongvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
unsigned long convmul,
unsigned long convdiv)
{
unsigned long *i, *min, *max;
int vleft, first = 1, err = 0;
size_t left;
char *p;
if (!table->data || !table->maxlen || !*lenp ||
(*ppos && SYSCTL_KERN_TO_USER(dir))) {
*lenp = 0;
return 0;
}
i = table->data;
min = table->extra1;
max = table->extra2;
vleft = table->maxlen / sizeof(unsigned long);
left = *lenp;
if (SYSCTL_USER_TO_KERN(dir)) {
if (proc_first_pos_non_zero_ignore(ppos, table))
goto out;
if (left > PAGE_SIZE - 1)
left = PAGE_SIZE - 1;
p = buffer;
}
for (; left && vleft--; i++, first = 0) {
unsigned long val;
if (SYSCTL_USER_TO_KERN(dir)) {
bool neg;
proc_skip_spaces(&p, &left);
if (!left)
break;
err = proc_get_long(&p, &left, &val, &neg,
proc_wspace_sep,
sizeof(proc_wspace_sep), NULL);
if (err || neg) {
err = -EINVAL;
break;
}
val = convmul * val / convdiv;
if ((min && val < *min) || (max && val > *max)) {
err = -EINVAL;
break;
}
WRITE_ONCE(*i, val);
} else {
val = convdiv * READ_ONCE(*i) / convmul;
if (!first)
proc_put_char(&buffer, &left, '\t');
proc_put_long(&buffer, &left, val, false);
}
}
if (SYSCTL_KERN_TO_USER(dir) && !first && left && !err)
proc_put_char(&buffer, &left, '\n');
if (SYSCTL_USER_TO_KERN(dir) && !err)
proc_skip_spaces(&p, &left);
if (SYSCTL_USER_TO_KERN(dir) && first)
return err ? : -EINVAL;
*lenp -= left;
out:
*ppos += *lenp;
return err;
}
int proc_doulongvec_minmax_conv(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
unsigned long convmul, unsigned long convdiv)
{
return do_proc_doulongvec_minmax(table, dir, buffer, lenp, ppos,
convmul, convdiv);
}
int proc_doulongvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return proc_doulongvec_minmax_conv(table, dir, buffer, lenp, ppos, 1l, 1l);
}
int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *table))
{
return do_proc_dointvec(table, dir, buffer, lenp, ppos, conv);
}
int proc_do_large_bitmap(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
int err = 0;
size_t left = *lenp;
unsigned long bitmap_len = table->maxlen;
unsigned long *bitmap = *(unsigned long **) table->data;
unsigned long *tmp_bitmap = NULL;
char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c = 0;
if (!bitmap || !bitmap_len || !left || (*ppos && SYSCTL_KERN_TO_USER(dir))) {
*lenp = 0;
return 0;
}
if (SYSCTL_USER_TO_KERN(dir)) {
char *p = buffer;
size_t skipped = 0;
if (left > PAGE_SIZE - 1) {
left = PAGE_SIZE - 1;
skipped = *lenp - left;
}
tmp_bitmap = bitmap_zalloc(bitmap_len, GFP_KERNEL);
if (!tmp_bitmap)
return -ENOMEM;
proc_skip_char(&p, &left, '\n');
while (!err && left) {
unsigned long val_a, val_b;
bool neg;
size_t saved_left;
saved_left = left;
err = proc_get_long(&p, &left, &val_a, &neg, tr_a,
sizeof(tr_a), &c);
if ((left <= 1) && skipped) {
left = saved_left;
break;
}
if (err)
break;
if (val_a >= bitmap_len || neg) {
err = -EINVAL;
break;
}
val_b = val_a;
if (left) {
p++;
left--;
}
if (c == '-') {
err = proc_get_long(&p, &left, &val_b,
&neg, tr_b, sizeof(tr_b),
&c);
if (!left && skipped) {
left = saved_left;
break;
}
if (err)
break;
if (val_b >= bitmap_len || neg ||
val_a > val_b) {
err = -EINVAL;
break;
}
if (left) {
p++;
left--;
}
}
bitmap_set(tmp_bitmap, val_a, val_b - val_a + 1);
proc_skip_char(&p, &left, '\n');
}
left += skipped;
} else {
unsigned long bit_a, bit_b = 0;
bool first = 1;
while (left) {
bit_a = find_next_bit(bitmap, bitmap_len, bit_b);
if (bit_a >= bitmap_len)
break;
bit_b = find_next_zero_bit(bitmap, bitmap_len,
bit_a + 1) - 1;
if (!first)
proc_put_char(&buffer, &left, ',');
proc_put_long(&buffer, &left, bit_a, false);
if (bit_a != bit_b) {
proc_put_char(&buffer, &left, '-');
proc_put_long(&buffer, &left, bit_b, false);
}
first = 0; bit_b++;
}
proc_put_char(&buffer, &left, '\n');
}
if (!err) {
if (SYSCTL_USER_TO_KERN(dir)) {
if (*ppos)
bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len);
else
bitmap_copy(bitmap, tmp_bitmap, bitmap_len);
}
*lenp -= left;
*ppos += *lenp;
}
bitmap_free(tmp_bitmap);
return err;
}
#else
int proc_dostring(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dobool(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_douintvec(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_douintvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp, unsigned int *valp,
int write, const struct ctl_table *table))
{
return -ENOSYS;
}
int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr)
{
return -ENOSYS;
}
int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr,
ulong (*u_ptr_op)(const ulong))
{
return -ENOSYS;
}
int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr),
int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr))
{
return -ENOSYS;
}
int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_doulongvec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_doulongvec_minmax_conv(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos,
unsigned long convmul, unsigned long convdiv)
{
return -ENOSYS;
}
int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *table))
{
return -ENOSYS;
}
int proc_do_large_bitmap(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
#endif
#if defined(CONFIG_SYSCTL)
int proc_do_static_key(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct static_key *key = (struct static_key *)table->data;
static DEFINE_MUTEX(static_key_mutex);
int val, ret;
struct ctl_table tmp = {
.data = &val,
.maxlen = sizeof(val),
.mode = table->mode,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
};
if (SYSCTL_USER_TO_KERN(dir) && !capable(CAP_SYS_ADMIN))
return -EPERM;
mutex_lock(&static_key_mutex);
val = static_key_enabled(key);
ret = proc_dointvec_minmax(&tmp, dir, buffer, lenp, ppos);
if (SYSCTL_USER_TO_KERN(dir) && !ret) {
if (val)
static_key_enable(key);
else
static_key_disable(key);
}
mutex_unlock(&static_key_mutex);
return ret;
}
static const struct ctl_table sysctl_subsys_table[] = {
#ifdef CONFIG_PROC_SYSCTL
{
.procname = "sysctl_writes_strict",
.data = &sysctl_writes_strict,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_NEG_ONE,
.extra2 = SYSCTL_ONE,
},
#endif
{
.procname = "ngroups_max",
.data = (void *)&ngroups_max,
.maxlen = sizeof (int),
.mode = 0444,
.proc_handler = proc_dointvec,
},
{
.procname = "cap_last_cap",
.data = (void *)&cap_last_cap,
.maxlen = sizeof(int),
.mode = 0444,
.proc_handler = proc_dointvec,
},
#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW
{
.procname = "unaligned-trap",
.data = &unaligned_enabled,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
#endif
#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN
{
.procname = "ignore-unaligned-usertrap",
.data = &no_unaligned_warning,
.maxlen = sizeof (int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
#endif
};
int __init sysctl_init_bases(void)
{
register_sysctl_init("kernel", sysctl_subsys_table);
return 0;
}
#endif
EXPORT_SYMBOL(proc_dobool);
EXPORT_SYMBOL(proc_dointvec);
EXPORT_SYMBOL(proc_douintvec);
EXPORT_SYMBOL(proc_dointvec_minmax);
EXPORT_SYMBOL_GPL(proc_douintvec_minmax);
EXPORT_SYMBOL(proc_dostring);
EXPORT_SYMBOL(proc_doulongvec_minmax);
EXPORT_SYMBOL(proc_do_large_bitmap);