#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); }