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); /* * Arrays are not supported, keep this simple. *Do not* add * support for them. */ 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);} /** * proc_douintvec_conv - read a vector of unsigned ints with a custom converter * * @table: the sysctl table * @dir: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * @conv: Custom converter call back * * Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer * values from/to the user buffer, treated as an ASCII string. Negative * strings are not allowed. * * Returns 0 on success */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);} /** * proc_dobool - read/write a bool * @table: the sysctl table * @dir: %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 one integer value from/to the user buffer, * treated as an ASCII string. * * table->data must point to a bool variable and table->maxlen must * be sizeof(bool). * * Returns 0 on success. */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; /* Do not support arrays yet. */ 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;} /** * proc_dointvec - read a vector of integers * @table: the sysctl table * @dir: %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(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * * Returns 0 on success. */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);} /** * proc_douintvec - read a vector of unsigned integers * @table: the sysctl table * @dir: %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(unsigned int) unsigned integer * values from/to the user buffer, treated as an ASCII string. * * Returns 0 on success. */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);} /** * proc_dointvec_minmax - read a vector of integers with min/max values * @table: the sysctl table * @dir: %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(unsigned int) integer * 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 or -EINVAL when the range check fails and * SYSCTL_USER_TO_KERN(dir) == true */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);} /** * proc_douintvec_minmax - read a vector of unsigned ints with min/max values * @table: the sysctl table * @dir: %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(unsigned int) unsigned integer * values from/to the user buffer, treated as an ASCII string. Negative * strings are not allowed. * * When changing the kernel variable, this routine will ensure the values * are within the range specified by table->extra1 (min) and table->extra2 * (max). And Check that the values are less than UINT_MAX to avoid having to * support wrap around uses from userspace. * * Returns 0 on success or -ERANGE when range check failes and * SYSCTL_USER_TO_KERN(dir) == true */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);} /** * proc_dou8vec_minmax - read a vector of unsigned chars with min/max values * @table: the sysctl table * @dir: %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(u8) unsigned chars * values from/to the user buffer, treated as an ASCII string. Negative * strings are not allowed. * * This routine will ensure the values are within the range specified by * table->extra1 (min) and table->extra2 (max). * * Returns 0 on success or an error on SYSCTL_USER_TO_KERN(dir) == true * and the range check fails. */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; /* Do not support arrays yet. */ 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; Lines 501–1000 of 1438. Previous lines Load more
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); /* * Arrays are not supported, keep this simple. *Do not* add * support for them. */ 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); } /** * proc_douintvec_conv - read a vector of unsigned ints with a custom converter * * @table: the sysctl table * @dir: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * @conv: Custom converter call back * * Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer * values from/to the user buffer, treated as an ASCII string. Negative * strings are not allowed. * * Returns 0 on success */ 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); } /** * proc_dobool - read/write a bool * @table: the sysctl table * @dir: %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 one integer value from/to the user buffer, * treated as an ASCII string. * * table->data must point to a bool variable and table->maxlen must * be sizeof(bool). * * Returns 0 on success. */ 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; /* Do not support arrays yet. */ 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; } /** * proc_dointvec - read a vector of integers * @table: the sysctl table * @dir: %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(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * * Returns 0 on success. */ 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); } /** * proc_douintvec - read a vector of unsigned integers * @table: the sysctl table * @dir: %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(unsigned int) unsigned integer * values from/to the user buffer, treated as an ASCII string. * * Returns 0 on success. */ 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); } /** * proc_dointvec_minmax - read a vector of integers with min/max values * @table: the sysctl table * @dir: %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(unsigned int) integer * 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 or -EINVAL when the range check fails and * SYSCTL_USER_TO_KERN(dir) == true */ 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); } /** * proc_douintvec_minmax - read a vector of unsigned ints with min/max values * @table: the sysctl table * @dir: %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(unsigned int) unsigned integer * values from/to the user buffer, treated as an ASCII string. Negative * strings are not allowed. * * When changing the kernel variable, this routine will ensure the values * are within the range specified by table->extra1 (min) and table->extra2 * (max). And Check that the values are less than UINT_MAX to avoid having to * support wrap around uses from userspace. * * Returns 0 on success or -ERANGE when range check failes and * SYSCTL_USER_TO_KERN(dir) == true */ 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); } /** * proc_dou8vec_minmax - read a vector of unsigned chars with min/max values * @table: the sysctl table * @dir: %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(u8) unsigned chars * values from/to the user buffer, treated as an ASCII string. Negative * strings are not allowed. * * This routine will ensure the values are within the range specified by * table->extra1 (min) and table->extra2 (max). * * Returns 0 on success or an error on SYSCTL_USER_TO_KERN(dir) == true * and the range check fails. */ 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; /* Do not support arrays yet. */ 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;