summaryrefslogtreecommitdiff
path: root/kernel/do_proc_dotype.h
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/do_proc_dotype.h')
-rw-r--r--kernel/do_proc_dotype.h213
1 files changed, 213 insertions, 0 deletions
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);
+}