From 7abc58c688b2dfee14cb7d43ab2d457299e35e47 Mon Sep 17 00:00:00 2001 From: John Denker Date: Tue, 8 Oct 2013 11:48:50 -0700 Subject: upgrade sysctl /proc/ interface to handle ulonglong --- kernel/do_proc_dotype.h | 213 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 kernel/do_proc_dotype.h (limited to 'kernel/do_proc_dotype.h') diff --git a/kernel/do_proc_dotype.h b/kernel/do_proc_dotype.h new file mode 100644 index 0000000..7d30ffe --- /dev/null +++ b/kernel/do_proc_dotype.h @@ -0,0 +1,213 @@ +#define __do_NAME(x) CAT3(__do_proc_do, x, vec_minmax) +#define do_NAME(x) CAT3(do_proc_do, x, vec_minmax) +#define NAME(x) CAT3(proc_do, x, vec_minmax) +#define get_NAME(x) CAT3(proc_get_, x,) +#define put_NAME(x) CAT3(proc_put_, x,) + +#define TMPBUFLEN 22 +/** + * proc_get_long - reads an ASCII formatted 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 exists (size is non-zero after returning from this + * function), @tr is updated with the trailing character. + */ +static int get_NAME(sTYPE)(char **buf, size_t *size, + uTYPE *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 = STR2TYPE (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 - converts an integer to a decimal ASCII formatted 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 written. + */ +static int put_NAME(sTYPE)(void __user **buf, size_t *size, + uTYPE val, bool neg) +{ + int len; + char tmp[TMPBUFLEN], *p = tmp; + + sprintf(p, OFMT, 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 __do_NAME(TYPE)(void *data, struct ctl_table *table, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos, + TYPE convmul, + TYPE convdiv) +{ + TYPE *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 = (TYPE *) data; + min = (TYPE *) table->extra1; + max = (TYPE *) table->extra2; + vleft = table->maxlen / sizeof(TYPE); + 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++, first = 0) { + TYPE val; + + if (write) { + bool neg; + + left -= proc_skip_spaces(&kbuf); + + err = get_NAME(sTYPE)(&kbuf, &left, &val, &neg, + proc_wspace_sep, + sizeof(proc_wspace_sep), NULL); + if (err) + break; + if (neg) + continue; + if ((min && val < *min) || (max && val > *max)) + continue; + *i = val; + } else { + val = convdiv * (*i) / convmul; + if (!first) + err = proc_put_char(&buffer, &left, '\t'); + err = put_NAME(sTYPE)(&buffer, &left, val, false); + if (err) + break; + } + } + + if (!write && !first && left && !err) + err = proc_put_char(&buffer, &left, '\n'); +// FIXME: Why is the following line different from the +// corresponding line in __do_proc_dointvec??? + if (write && !err) + left -= proc_skip_spaces(&kbuf); +free: + if (write) { + free_page(page); + if (first) + return err ? : -EINVAL; + } + *lenp -= left; + *ppos += *lenp; + return err; +} + +static int do_NAME(TYPE)(struct ctl_table *table, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos, + TYPE convmul, + TYPE convdiv) +{ + return __do_NAME(TYPE)(table->data, table, write, + buffer, lenp, ppos, convmul, convdiv); +} + +/** + * proc_doulongvec_minmax - read a vector of long integers with min/max values + * @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 + * + * Reads/writes up to table->maxlen/sizeof(TYPE) variables of type TYPE + * values from/to the user buffer, treated as an ASCII string. + * + * This routine will ensure the values are within the range specified by + * table->extra1 (min) and table->extra2 (max). + * + * Returns 0 on success. + */ +int NAME(TYPE)(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + return do_NAME(TYPE)(table, write, buffer, lenp, ppos, 1l, 1l); +} -- cgit v1.2.3