Merge branch 'master' into for-2.6.35
Conflicts: fs/ext3/fsync.c Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
This commit is contained in:
597
kernel/sysctl.c
597
kernel/sysctl.c
@@ -164,6 +164,27 @@ static int proc_taint(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
static int __sysrq_enabled; /* Note: sysrq code ises it's own private copy */
|
||||
|
||||
static int sysrq_sysctl_handler(ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = proc_dointvec(table, write, buffer, lenp, ppos);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (write)
|
||||
sysrq_toggle_support(__sysrq_enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static struct ctl_table root_table[];
|
||||
static struct ctl_table_root sysctl_table_root;
|
||||
static struct ctl_table_header root_table_header = {
|
||||
@@ -568,7 +589,7 @@ static struct ctl_table kern_table[] = {
|
||||
.data = &__sysrq_enabled,
|
||||
.maxlen = sizeof (int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
.proc_handler = sysrq_sysctl_handler,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_SYSCTL
|
||||
@@ -622,7 +643,7 @@ static struct ctl_table kern_table[] = {
|
||||
#endif
|
||||
{
|
||||
.procname = "userprocess_debug",
|
||||
.data = &sysctl_userprocess_debug,
|
||||
.data = &show_unhandled_signals,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec,
|
||||
@@ -1440,7 +1461,8 @@ static struct ctl_table fs_table[] = {
|
||||
};
|
||||
|
||||
static struct ctl_table debug_table[] = {
|
||||
#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC)
|
||||
#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || \
|
||||
defined(CONFIG_S390)
|
||||
{
|
||||
.procname = "exception-trace",
|
||||
.data = &show_unhandled_signals,
|
||||
@@ -2049,8 +2071,132 @@ int proc_dostring(struct ctl_table *table, int write,
|
||||
buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
static size_t proc_skip_spaces(char **buf)
|
||||
{
|
||||
size_t ret;
|
||||
char *tmp = skip_spaces(*buf);
|
||||
ret = tmp - *buf;
|
||||
*buf = tmp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
|
||||
static void proc_skip_char(char **buf, size_t *size, const char v)
|
||||
{
|
||||
while (*size) {
|
||||
if (**buf != v)
|
||||
break;
|
||||
(*size)--;
|
||||
(*buf)++;
|
||||
}
|
||||
}
|
||||
|
||||
#define TMPBUFLEN 22
|
||||
/**
|
||||
* proc_get_long - reads an ASCII formated integer from a user buffer
|
||||
*
|
||||
* @buf - a kernel buffer
|
||||
* @size - size of the kernel buffer
|
||||
* @val - this is where the number will be stored
|
||||
* @neg - set to %TRUE if number is negative
|
||||
* @perm_tr - a vector which contains the allowed trailers
|
||||
* @perm_tr_len - size of the perm_tr vector
|
||||
* @tr - pointer to store the trailer character
|
||||
*
|
||||
* In case of success 0 is returned and buf and size are updated with
|
||||
* the amount of bytes read. If tr is non NULL and a trailing
|
||||
* character exist (size is non zero after returning from this
|
||||
* function) tr is updated with the trailing character.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
int len;
|
||||
char *p, tmp[TMPBUFLEN];
|
||||
|
||||
if (!*size)
|
||||
return -EINVAL;
|
||||
|
||||
len = *size;
|
||||
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;
|
||||
|
||||
*val = simple_strtoul(p, &p, 0);
|
||||
|
||||
len = p - tmp;
|
||||
|
||||
/* We don't know if the next char is whitespace thus we may accept
|
||||
* invalid integers (e.g. 1234...a) or two integers instead of one
|
||||
* (e.g. 123...1). So lets not allow such large numbers. */
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* proc_put_long - coverts an integer to a decimal ASCII formated string
|
||||
*
|
||||
* @buf - the user buffer
|
||||
* @size - the size of the user buffer
|
||||
* @val - the integer to be converted
|
||||
* @neg - sign of the number, %TRUE for negative
|
||||
*
|
||||
* In case of success 0 is returned and buf and size are updated with
|
||||
* the amount of bytes read.
|
||||
*/
|
||||
static int proc_put_long(void __user **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;
|
||||
if (copy_to_user(*buf, tmp, len))
|
||||
return -EFAULT;
|
||||
*size -= len;
|
||||
*buf += len;
|
||||
return 0;
|
||||
}
|
||||
#undef TMPBUFLEN
|
||||
|
||||
static int proc_put_char(void __user **buf, size_t *size, char c)
|
||||
{
|
||||
if (*size) {
|
||||
char __user **buffer = (char __user **)buf;
|
||||
if (put_user(c, *buffer))
|
||||
return -EFAULT;
|
||||
(*size)--, (*buffer)++;
|
||||
*buf = *buffer;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
|
||||
int *valp,
|
||||
int write, void *data)
|
||||
{
|
||||
@@ -2059,33 +2205,31 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
|
||||
} else {
|
||||
int val = *valp;
|
||||
if (val < 0) {
|
||||
*negp = -1;
|
||||
*negp = true;
|
||||
*lvalp = (unsigned long)-val;
|
||||
} else {
|
||||
*negp = 0;
|
||||
*negp = false;
|
||||
*lvalp = (unsigned long)val;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
|
||||
|
||||
static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
|
||||
int write, void __user *buffer,
|
||||
size_t *lenp, loff_t *ppos,
|
||||
int (*conv)(int *negp, unsigned long *lvalp, int *valp,
|
||||
int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
|
||||
int write, void *data),
|
||||
void *data)
|
||||
{
|
||||
#define TMPBUFLEN 21
|
||||
int *i, vleft, first = 1, neg;
|
||||
unsigned long lval;
|
||||
size_t left, len;
|
||||
int *i, vleft, first = 1, err = 0;
|
||||
unsigned long page = 0;
|
||||
size_t left;
|
||||
char *kbuf;
|
||||
|
||||
char buf[TMPBUFLEN], *p;
|
||||
char __user *s = buffer;
|
||||
|
||||
if (!tbl_data || !table->maxlen || !*lenp ||
|
||||
(*ppos && !write)) {
|
||||
if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
@@ -2097,89 +2241,69 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
|
||||
if (!conv)
|
||||
conv = do_proc_dointvec_conv;
|
||||
|
||||
for (; left && vleft--; i++, first=0) {
|
||||
if (write) {
|
||||
while (left) {
|
||||
char c;
|
||||
if (get_user(c, s))
|
||||
return -EFAULT;
|
||||
if (!isspace(c))
|
||||
break;
|
||||
left--;
|
||||
s++;
|
||||
}
|
||||
if (!left)
|
||||
break;
|
||||
neg = 0;
|
||||
len = left;
|
||||
if (len > sizeof(buf) - 1)
|
||||
len = sizeof(buf) - 1;
|
||||
if (copy_from_user(buf, s, len))
|
||||
return -EFAULT;
|
||||
buf[len] = 0;
|
||||
p = buf;
|
||||
if (*p == '-' && left > 1) {
|
||||
neg = 1;
|
||||
p++;
|
||||
}
|
||||
if (*p < '0' || *p > '9')
|
||||
break;
|
||||
|
||||
lval = simple_strtoul(p, &p, 0);
|
||||
|
||||
len = p-buf;
|
||||
if ((len < left) && *p && !isspace(*p))
|
||||
break;
|
||||
s += len;
|
||||
left -= len;
|
||||
|
||||
if (conv(&neg, &lval, i, 1, data))
|
||||
break;
|
||||
} else {
|
||||
p = buf;
|
||||
if (!first)
|
||||
*p++ = '\t';
|
||||
|
||||
if (conv(&neg, &lval, i, 0, data))
|
||||
break;
|
||||
|
||||
sprintf(p, "%s%lu", neg ? "-" : "", lval);
|
||||
len = strlen(buf);
|
||||
if (len > left)
|
||||
len = left;
|
||||
if(copy_to_user(s, buf, len))
|
||||
return -EFAULT;
|
||||
left -= len;
|
||||
s += len;
|
||||
}
|
||||
}
|
||||
|
||||
if (!write && !first && left) {
|
||||
if(put_user('\n', s))
|
||||
return -EFAULT;
|
||||
left--, s++;
|
||||
}
|
||||
if (write) {
|
||||
while (left) {
|
||||
char c;
|
||||
if (get_user(c, s++))
|
||||
return -EFAULT;
|
||||
if (!isspace(c))
|
||||
if (left > PAGE_SIZE - 1)
|
||||
left = PAGE_SIZE - 1;
|
||||
page = __get_free_page(GFP_TEMPORARY);
|
||||
kbuf = (char *) page;
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(kbuf, buffer, left)) {
|
||||
err = -EFAULT;
|
||||
goto free;
|
||||
}
|
||||
kbuf[left] = 0;
|
||||
}
|
||||
|
||||
for (; left && vleft--; i++, first=0) {
|
||||
unsigned long lval;
|
||||
bool neg;
|
||||
|
||||
if (write) {
|
||||
left -= proc_skip_spaces(&kbuf);
|
||||
|
||||
err = proc_get_long(&kbuf, &left, &lval, &neg,
|
||||
proc_wspace_sep,
|
||||
sizeof(proc_wspace_sep), NULL);
|
||||
if (err)
|
||||
break;
|
||||
if (conv(&neg, &lval, i, 1, data)) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (conv(&neg, &lval, i, 0, data)) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (!first)
|
||||
err = proc_put_char(&buffer, &left, '\t');
|
||||
if (err)
|
||||
break;
|
||||
err = proc_put_long(&buffer, &left, lval, neg);
|
||||
if (err)
|
||||
break;
|
||||
left--;
|
||||
}
|
||||
}
|
||||
if (write && first)
|
||||
return -EINVAL;
|
||||
|
||||
if (!write && !first && left && !err)
|
||||
err = proc_put_char(&buffer, &left, '\n');
|
||||
if (write && !err)
|
||||
left -= proc_skip_spaces(&kbuf);
|
||||
free:
|
||||
if (write) {
|
||||
free_page(page);
|
||||
if (first)
|
||||
return err ? : -EINVAL;
|
||||
}
|
||||
*lenp -= left;
|
||||
*ppos += *lenp;
|
||||
return 0;
|
||||
#undef TMPBUFLEN
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_proc_dointvec(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos,
|
||||
int (*conv)(int *negp, unsigned long *lvalp, int *valp,
|
||||
int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
|
||||
int write, void *data),
|
||||
void *data)
|
||||
{
|
||||
@@ -2247,8 +2371,8 @@ struct do_proc_dointvec_minmax_conv_param {
|
||||
int *max;
|
||||
};
|
||||
|
||||
static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp,
|
||||
int *valp,
|
||||
static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
|
||||
int *valp,
|
||||
int write, void *data)
|
||||
{
|
||||
struct do_proc_dointvec_minmax_conv_param *param = data;
|
||||
@@ -2261,10 +2385,10 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp,
|
||||
} else {
|
||||
int val = *valp;
|
||||
if (val < 0) {
|
||||
*negp = -1;
|
||||
*negp = true;
|
||||
*lvalp = (unsigned long)-val;
|
||||
} else {
|
||||
*negp = 0;
|
||||
*negp = false;
|
||||
*lvalp = (unsigned long)val;
|
||||
}
|
||||
}
|
||||
@@ -2304,102 +2428,78 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
|
||||
unsigned long convmul,
|
||||
unsigned long convdiv)
|
||||
{
|
||||
#define TMPBUFLEN 21
|
||||
unsigned long *i, *min, *max, val;
|
||||
int vleft, first=1, neg;
|
||||
size_t len, left;
|
||||
char buf[TMPBUFLEN], *p;
|
||||
char __user *s = buffer;
|
||||
|
||||
if (!data || !table->maxlen || !*lenp ||
|
||||
(*ppos && !write)) {
|
||||
unsigned long *i, *min, *max;
|
||||
int vleft, first = 1, err = 0;
|
||||
unsigned long page = 0;
|
||||
size_t left;
|
||||
char *kbuf;
|
||||
|
||||
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
i = (unsigned long *) data;
|
||||
min = (unsigned long *) table->extra1;
|
||||
max = (unsigned long *) table->extra2;
|
||||
vleft = table->maxlen / sizeof(unsigned long);
|
||||
left = *lenp;
|
||||
|
||||
|
||||
if (write) {
|
||||
if (left > PAGE_SIZE - 1)
|
||||
left = PAGE_SIZE - 1;
|
||||
page = __get_free_page(GFP_TEMPORARY);
|
||||
kbuf = (char *) page;
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(kbuf, buffer, left)) {
|
||||
err = -EFAULT;
|
||||
goto free;
|
||||
}
|
||||
kbuf[left] = 0;
|
||||
}
|
||||
|
||||
for (; left && vleft--; i++, min++, max++, first=0) {
|
||||
unsigned long val;
|
||||
|
||||
if (write) {
|
||||
while (left) {
|
||||
char c;
|
||||
if (get_user(c, s))
|
||||
return -EFAULT;
|
||||
if (!isspace(c))
|
||||
break;
|
||||
left--;
|
||||
s++;
|
||||
}
|
||||
if (!left)
|
||||
break;
|
||||
neg = 0;
|
||||
len = left;
|
||||
if (len > TMPBUFLEN-1)
|
||||
len = TMPBUFLEN-1;
|
||||
if (copy_from_user(buf, s, len))
|
||||
return -EFAULT;
|
||||
buf[len] = 0;
|
||||
p = buf;
|
||||
if (*p == '-' && left > 1) {
|
||||
neg = 1;
|
||||
p++;
|
||||
}
|
||||
if (*p < '0' || *p > '9')
|
||||
break;
|
||||
val = simple_strtoul(p, &p, 0) * convmul / convdiv ;
|
||||
len = p-buf;
|
||||
if ((len < left) && *p && !isspace(*p))
|
||||
bool neg;
|
||||
|
||||
left -= proc_skip_spaces(&kbuf);
|
||||
|
||||
err = proc_get_long(&kbuf, &left, &val, &neg,
|
||||
proc_wspace_sep,
|
||||
sizeof(proc_wspace_sep), NULL);
|
||||
if (err)
|
||||
break;
|
||||
if (neg)
|
||||
val = -val;
|
||||
s += len;
|
||||
left -= len;
|
||||
|
||||
if(neg)
|
||||
continue;
|
||||
if ((min && val < *min) || (max && val > *max))
|
||||
continue;
|
||||
*i = val;
|
||||
} else {
|
||||
p = buf;
|
||||
val = convdiv * (*i) / convmul;
|
||||
if (!first)
|
||||
*p++ = '\t';
|
||||
sprintf(p, "%lu", convdiv * (*i) / convmul);
|
||||
len = strlen(buf);
|
||||
if (len > left)
|
||||
len = left;
|
||||
if(copy_to_user(s, buf, len))
|
||||
return -EFAULT;
|
||||
left -= len;
|
||||
s += len;
|
||||
err = proc_put_char(&buffer, &left, '\t');
|
||||
err = proc_put_long(&buffer, &left, val, false);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!write && !first && left) {
|
||||
if(put_user('\n', s))
|
||||
return -EFAULT;
|
||||
left--, s++;
|
||||
}
|
||||
if (!write && !first && left && !err)
|
||||
err = proc_put_char(&buffer, &left, '\n');
|
||||
if (write && !err)
|
||||
left -= proc_skip_spaces(&kbuf);
|
||||
free:
|
||||
if (write) {
|
||||
while (left) {
|
||||
char c;
|
||||
if (get_user(c, s++))
|
||||
return -EFAULT;
|
||||
if (!isspace(c))
|
||||
break;
|
||||
left--;
|
||||
}
|
||||
free_page(page);
|
||||
if (first)
|
||||
return err ? : -EINVAL;
|
||||
}
|
||||
if (write && first)
|
||||
return -EINVAL;
|
||||
*lenp -= left;
|
||||
*ppos += *lenp;
|
||||
return 0;
|
||||
#undef TMPBUFLEN
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
|
||||
@@ -2460,7 +2560,7 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
|
||||
}
|
||||
|
||||
|
||||
static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
|
||||
static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
|
||||
int *valp,
|
||||
int write, void *data)
|
||||
{
|
||||
@@ -2472,10 +2572,10 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
|
||||
int val = *valp;
|
||||
unsigned long lval;
|
||||
if (val < 0) {
|
||||
*negp = -1;
|
||||
*negp = true;
|
||||
lval = (unsigned long)-val;
|
||||
} else {
|
||||
*negp = 0;
|
||||
*negp = false;
|
||||
lval = (unsigned long)val;
|
||||
}
|
||||
*lvalp = lval / HZ;
|
||||
@@ -2483,7 +2583,7 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
|
||||
static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp,
|
||||
int *valp,
|
||||
int write, void *data)
|
||||
{
|
||||
@@ -2495,10 +2595,10 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
|
||||
int val = *valp;
|
||||
unsigned long lval;
|
||||
if (val < 0) {
|
||||
*negp = -1;
|
||||
*negp = true;
|
||||
lval = (unsigned long)-val;
|
||||
} else {
|
||||
*negp = 0;
|
||||
*negp = false;
|
||||
lval = (unsigned long)val;
|
||||
}
|
||||
*lvalp = jiffies_to_clock_t(lval);
|
||||
@@ -2506,7 +2606,7 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp,
|
||||
static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
|
||||
int *valp,
|
||||
int write, void *data)
|
||||
{
|
||||
@@ -2516,10 +2616,10 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp,
|
||||
int val = *valp;
|
||||
unsigned long lval;
|
||||
if (val < 0) {
|
||||
*negp = -1;
|
||||
*negp = true;
|
||||
lval = (unsigned long)-val;
|
||||
} else {
|
||||
*negp = 0;
|
||||
*negp = false;
|
||||
lval = (unsigned long)val;
|
||||
}
|
||||
*lvalp = jiffies_to_msecs(lval);
|
||||
@@ -2616,6 +2716,157 @@ static int proc_do_cad_pid(struct ctl_table *table, int write,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* proc_do_large_bitmap - read/write from/to a large bitmap
|
||||
* @table: the sysctl table
|
||||
* @write: %TRUE if this is a write to the sysctl file
|
||||
* @buffer: the user buffer
|
||||
* @lenp: the size of the user buffer
|
||||
* @ppos: file position
|
||||
*
|
||||
* The bitmap is stored at table->data and the bitmap length (in bits)
|
||||
* in table->maxlen.
|
||||
*
|
||||
* We use a range comma separated format (e.g. 1,3-4,10-10) so that
|
||||
* large bitmaps may be represented in a compact manner. Writing into
|
||||
* the file will clear the bitmap then update it with the given input.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int proc_do_large_bitmap(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int err = 0;
|
||||
bool first = 1;
|
||||
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;
|
||||
|
||||
if (!bitmap_len || !left || (*ppos && !write)) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (write) {
|
||||
unsigned long page = 0;
|
||||
char *kbuf;
|
||||
|
||||
if (left > PAGE_SIZE - 1)
|
||||
left = PAGE_SIZE - 1;
|
||||
|
||||
page = __get_free_page(GFP_TEMPORARY);
|
||||
kbuf = (char *) page;
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(kbuf, buffer, left)) {
|
||||
free_page(page);
|
||||
return -EFAULT;
|
||||
}
|
||||
kbuf[left] = 0;
|
||||
|
||||
tmp_bitmap = kzalloc(BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long),
|
||||
GFP_KERNEL);
|
||||
if (!tmp_bitmap) {
|
||||
free_page(page);
|
||||
return -ENOMEM;
|
||||
}
|
||||
proc_skip_char(&kbuf, &left, '\n');
|
||||
while (!err && left) {
|
||||
unsigned long val_a, val_b;
|
||||
bool neg;
|
||||
|
||||
err = proc_get_long(&kbuf, &left, &val_a, &neg, tr_a,
|
||||
sizeof(tr_a), &c);
|
||||
if (err)
|
||||
break;
|
||||
if (val_a >= bitmap_len || neg) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
val_b = val_a;
|
||||
if (left) {
|
||||
kbuf++;
|
||||
left--;
|
||||
}
|
||||
|
||||
if (c == '-') {
|
||||
err = proc_get_long(&kbuf, &left, &val_b,
|
||||
&neg, tr_b, sizeof(tr_b),
|
||||
&c);
|
||||
if (err)
|
||||
break;
|
||||
if (val_b >= bitmap_len || neg ||
|
||||
val_a > val_b) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (left) {
|
||||
kbuf++;
|
||||
left--;
|
||||
}
|
||||
}
|
||||
|
||||
while (val_a <= val_b)
|
||||
set_bit(val_a++, tmp_bitmap);
|
||||
|
||||
first = 0;
|
||||
proc_skip_char(&kbuf, &left, '\n');
|
||||
}
|
||||
free_page(page);
|
||||
} else {
|
||||
unsigned long bit_a, bit_b = 0;
|
||||
|
||||
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) {
|
||||
err = proc_put_char(&buffer, &left, ',');
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
err = proc_put_long(&buffer, &left, bit_a, false);
|
||||
if (err)
|
||||
break;
|
||||
if (bit_a != bit_b) {
|
||||
err = proc_put_char(&buffer, &left, '-');
|
||||
if (err)
|
||||
break;
|
||||
err = proc_put_long(&buffer, &left, bit_b, false);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
first = 0; bit_b++;
|
||||
}
|
||||
if (!err)
|
||||
err = proc_put_char(&buffer, &left, '\n');
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
if (write) {
|
||||
if (*ppos)
|
||||
bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len);
|
||||
else
|
||||
memcpy(bitmap, tmp_bitmap,
|
||||
BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long));
|
||||
}
|
||||
kfree(tmp_bitmap);
|
||||
*lenp -= left;
|
||||
*ppos += *lenp;
|
||||
return 0;
|
||||
} else {
|
||||
kfree(tmp_bitmap);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_PROC_FS */
|
||||
|
||||
int proc_dostring(struct ctl_table *table, int write,
|
||||
|
||||
Reference in New Issue
Block a user