diff options
| author | John Denker <jsd@av8n.com> | 2013-10-08 11:48:50 -0700 | 
|---|---|---|
| committer | John Denker <jsd@av8n.com> | 2013-10-18 05:32:26 -0700 | 
| commit | 7abc58c688b2dfee14cb7d43ab2d457299e35e47 (patch) | |
| tree | d9de8cb831060d26ee5f4beda28553af323f0991 | |
| parent | 17cdcc57fab1a27229751d2ddde59ee1f3749af8 (diff) | |
upgrade sysctl /proc/ interface to handle ulonglong
| -rw-r--r-- | include/linux/sysctl.h | 2 | ||||
| -rw-r--r-- | kernel/do_proc_dotype.h | 213 | ||||
| -rw-r--r-- | kernel/sysctl.c | 259 | 
3 files changed, 258 insertions, 216 deletions
| diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 14a8ff2..a4a8098 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -53,6 +53,8 @@ extern int proc_dointvec_ms_jiffies(struct ctl_table *, int,  				    void __user *, size_t *, loff_t *);  extern int proc_doulongvec_minmax(struct ctl_table *, int,  				  void __user *, size_t *, loff_t *); +extern int proc_doulonglongvec_minmax(struct ctl_table *, int, +				  void __user *, size_t *, loff_t *);  extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int,  				      void __user *, size_t *, loff_t *);  extern int proc_do_large_bitmap(struct ctl_table *, int, 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); +} diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 07f6fc4..a97f159 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1122,7 +1122,7 @@ static struct ctl_table vm_table[] = {  		.proc_handler	= proc_dointvec,  	},  	{ -		.procname	= "page-cluster",  +		.procname	= "page-cluster",  		.data		= &page_cluster,  		.maxlen		= sizeof(int),  		.mode		= 0644, @@ -1580,7 +1580,7 @@ static struct ctl_table fs_table[] = {  		.mode		= 0555,  		.child		= inotify_table,  	}, -#endif	 +#endif  #ifdef CONFIG_EPOLL  	{  		.procname	= "epoll", @@ -1780,100 +1780,6 @@ static void proc_skip_char(char **buf, size_t *size, const char v)  	}  } -#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 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 - 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 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) { @@ -1907,6 +1813,37 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,  static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; +#define CAT3x(a,b,c) a ## b ## c +#define CAT3(a,b,c) CAT3x(a,b,c) + +/* per-TYPE stuff: */ +#define TYPE  ulong +#define sTYPE  long          /* signed version of TYPE */ +#define uTYPE ulong          /* unsgined version of TYPE */ +#define STR2TYPE simple_strtoul +#define OFMT "%s%lu" +#include "do_proc_dotype.h" +#undef OFMT +#undef STR2TYPE +#undef sTYPE +#undef uTYPE +#undef TYPE + +typedef          long long int longlong; +typedef unsigned long long int ulonglong; + +#define TYPE  ulonglong +#define sTYPE  longlong +#define uTYPE ulonglong +#define STR2TYPE simple_strtoull +#define OFMT "%s%llu" +#include "do_proc_dotype.h" +#undef OFMT +#undef STR2TYPE +#undef sTYPE +#undef uTYPE +#undef TYPE +  static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,  		  int write, void __user *buffer,  		  size_t *lenp, loff_t *ppos, @@ -1918,12 +1855,12 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,  	unsigned long page = 0;  	size_t left;  	char *kbuf; -	 +  	if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {  		*lenp = 0;  		return 0;  	} -	 +  	i = (int *) tbl_data;  	vleft = table->maxlen / sizeof(*i);  	left = *lenp; @@ -1980,6 +1917,8 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,  	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_doulongvec_minmax???  	if (write && !err && left)  		left -= proc_skip_spaces(&kbuf);  free: @@ -2012,7 +1951,7 @@ static int do_proc_dointvec(struct ctl_table *table, int write,   * @ppos: file position   *   * Reads/writes up to table->maxlen/sizeof(unsigned int) integer - * values from/to the user buffer, treated as an ASCII string.  + * values from/to the user buffer, treated as an ASCII string.   *   * Returns 0 on success.   */ @@ -2157,118 +2096,6 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,  }  #endif -static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write, -				     void __user *buffer, -				     size_t *lenp, loff_t *ppos, -				     unsigned long convmul, -				     unsigned long convdiv) -{ -	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++, first = 0) { -		unsigned long val; - -		if (write) { -			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) -				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 = proc_put_long(&buffer, &left, val, false); -			if (err) -				break; -		} -	} - -	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 err; -} - -static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, -				     void __user *buffer, -				     size_t *lenp, loff_t *ppos, -				     unsigned long convmul, -				     unsigned long convdiv) -{ -	return __do_proc_doulongvec_minmax(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(unsigned long) unsigned long - * 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 proc_doulongvec_minmax(struct ctl_table *table, int write, -			   void __user *buffer, size_t *lenp, loff_t *ppos) -{ -    return do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos, 1l, 1l); -} -  /**   * proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values   * @table: the sysctl table @@ -2375,7 +2202,7 @@ static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,   * @ppos: file position   *   * Reads/writes up to table->maxlen/sizeof(unsigned int) integer - * values from/to the user buffer, treated as an ASCII string.  + * values from/to the user buffer, treated as an ASCII string.   * The values read are assumed to be in seconds, and are converted into   * jiffies.   * @@ -2397,8 +2224,8 @@ int proc_dointvec_jiffies(struct ctl_table *table, int write,   * @ppos: pointer to the file position   *   * Reads/writes up to table->maxlen/sizeof(unsigned int) integer - * values from/to the user buffer, treated as an ASCII string.  - * The values read are assumed to be in 1/USER_HZ seconds, and  + * values from/to the user buffer, treated as an ASCII string. + * The values read are assumed to be in 1/USER_HZ seconds, and   * are converted into jiffies.   *   * Returns 0 on success. @@ -2420,8 +2247,8 @@ int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write,   * @ppos: the current position in the file   *   * Reads/writes up to table->maxlen/sizeof(unsigned int) integer - * values from/to the user buffer, treated as an ASCII string.  - * The values read are assumed to be in 1/1000 seconds, and  + * values from/to the user buffer, treated as an ASCII string. + * The values read are assumed to be in 1/1000 seconds, and   * are converted into jiffies.   *   * Returns 0 on success. | 
