aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorAmerigo Wang <amwang@redhat.com>2010-05-04 20:26:45 -0400
committerDavid S. Miller <davem@davemloft.net>2010-05-16 02:28:38 -0400
commit00b7c3395aec3df43de5bd02a3c5a099ca51169f (patch)
tree6e4a1ec71a1cedadab752bdbfc25ddbf53202447 /kernel
parent1cdc5abf40c561982d2f7b06bcff17f9496309a5 (diff)
sysctl: refactor integer handling proc code
(Based on Octavian's work, and I modified a lot.) As we are about to add another integer handling proc function a little bit of cleanup is in order: add a few helper functions to improve code readability and decrease code duplication. In the process a bug is also fixed: if the user specifies a number with more then 20 digits it will be interpreted as two integers (e.g. 10000...13 will be interpreted as 100.... and 13). Behavior for EFAULT handling was changed as well. Previous to this patch, when an EFAULT error occurred in the middle of a write operation, although some of the elements were set, that was not acknowledged to the user (by shorting the write and returning the number of bytes accepted). EFAULT is now treated just like any other errors by acknowledging the amount of bytes accepted. Signed-off-by: Octavian Purdila <opurdila@ixiacom.com> Signed-off-by: WANG Cong <amwang@redhat.com> Cc: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/sysctl.c390
1 files changed, 229 insertions, 161 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 8686b0f5fc12..4a976208de29 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2040,8 +2040,122 @@ int proc_dostring(struct ctl_table *table, int write,
2040 buffer, lenp, ppos); 2040 buffer, lenp, ppos);
2041} 2041}
2042 2042
2043static size_t proc_skip_spaces(char **buf)
2044{
2045 size_t ret;
2046 char *tmp = skip_spaces(*buf);
2047 ret = tmp - *buf;
2048 *buf = tmp;
2049 return ret;
2050}
2051
2052#define TMPBUFLEN 22
2053/**
2054 * proc_get_long - reads an ASCII formated integer from a user buffer
2055 *
2056 * @buf - a kernel buffer
2057 * @size - size of the kernel buffer
2058 * @val - this is where the number will be stored
2059 * @neg - set to %TRUE if number is negative
2060 * @perm_tr - a vector which contains the allowed trailers
2061 * @perm_tr_len - size of the perm_tr vector
2062 * @tr - pointer to store the trailer character
2063 *
2064 * In case of success 0 is returned and buf and size are updated with
2065 * the amount of bytes read. If tr is non NULL and a trailing
2066 * character exist (size is non zero after returning from this
2067 * function) tr is updated with the trailing character.
2068 */
2069static int proc_get_long(char **buf, size_t *size,
2070 unsigned long *val, bool *neg,
2071 const char *perm_tr, unsigned perm_tr_len, char *tr)
2072{
2073 int len;
2074 char *p, tmp[TMPBUFLEN];
2075
2076 if (!*size)
2077 return -EINVAL;
2078
2079 len = *size;
2080 if (len > TMPBUFLEN - 1)
2081 len = TMPBUFLEN - 1;
2082
2083 memcpy(tmp, *buf, len);
2084
2085 tmp[len] = 0;
2086 p = tmp;
2087 if (*p == '-' && *size > 1) {
2088 *neg = true;
2089 p++;
2090 } else
2091 *neg = false;
2092 if (!isdigit(*p))
2093 return -EINVAL;
2094
2095 *val = simple_strtoul(p, &p, 0);
2096
2097 len = p - tmp;
2098
2099 /* We don't know if the next char is whitespace thus we may accept
2100 * invalid integers (e.g. 1234...a) or two integers instead of one
2101 * (e.g. 123...1). So lets not allow such large numbers. */
2102 if (len == TMPBUFLEN - 1)
2103 return -EINVAL;
2104
2105 if (len < *size && perm_tr_len && !memchr(perm_tr, *p, perm_tr_len))
2106 return -EINVAL;
2043 2107
2044static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, 2108 if (tr && (len < *size))
2109 *tr = *p;
2110
2111 *buf += len;
2112 *size -= len;
2113
2114 return 0;
2115}
2116
2117/**
2118 * proc_put_long - coverts an integer to a decimal ASCII formated string
2119 *
2120 * @buf - the user buffer
2121 * @size - the size of the user buffer
2122 * @val - the integer to be converted
2123 * @neg - sign of the number, %TRUE for negative
2124 *
2125 * In case of success 0 is returned and buf and size are updated with
2126 * the amount of bytes read.
2127 */
2128static int proc_put_long(void __user **buf, size_t *size, unsigned long val,
2129 bool neg)
2130{
2131 int len;
2132 char tmp[TMPBUFLEN], *p = tmp;
2133
2134 sprintf(p, "%s%lu", neg ? "-" : "", val);
2135 len = strlen(tmp);
2136 if (len > *size)
2137 len = *size;
2138 if (copy_to_user(*buf, tmp, len))
2139 return -EFAULT;
2140 *size -= len;
2141 *buf += len;
2142 return 0;
2143}
2144#undef TMPBUFLEN
2145
2146static int proc_put_char(void __user **buf, size_t *size, char c)
2147{
2148 if (*size) {
2149 char __user **buffer = (char __user **)buf;
2150 if (put_user(c, *buffer))
2151 return -EFAULT;
2152 (*size)--, (*buffer)++;
2153 *buf = *buffer;
2154 }
2155 return 0;
2156}
2157
2158static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
2045 int *valp, 2159 int *valp,
2046 int write, void *data) 2160 int write, void *data)
2047{ 2161{
@@ -2050,33 +2164,31 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp,
2050 } else { 2164 } else {
2051 int val = *valp; 2165 int val = *valp;
2052 if (val < 0) { 2166 if (val < 0) {
2053 *negp = -1; 2167 *negp = true;
2054 *lvalp = (unsigned long)-val; 2168 *lvalp = (unsigned long)-val;
2055 } else { 2169 } else {
2056 *negp = 0; 2170 *negp = false;
2057 *lvalp = (unsigned long)val; 2171 *lvalp = (unsigned long)val;
2058 } 2172 }
2059 } 2173 }
2060 return 0; 2174 return 0;
2061} 2175}
2062 2176
2177static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
2178
2063static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, 2179static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
2064 int write, void __user *buffer, 2180 int write, void __user *buffer,
2065 size_t *lenp, loff_t *ppos, 2181 size_t *lenp, loff_t *ppos,
2066 int (*conv)(int *negp, unsigned long *lvalp, int *valp, 2182 int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
2067 int write, void *data), 2183 int write, void *data),
2068 void *data) 2184 void *data)
2069{ 2185{
2070#define TMPBUFLEN 21 2186 int *i, vleft, first = 1, err = 0;
2071 int *i, vleft, first = 1, neg; 2187 unsigned long page = 0;
2072 unsigned long lval; 2188 size_t left;
2073 size_t left, len; 2189 char *kbuf;
2074
2075 char buf[TMPBUFLEN], *p;
2076 char __user *s = buffer;
2077 2190
2078 if (!tbl_data || !table->maxlen || !*lenp || 2191 if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {
2079 (*ppos && !write)) {
2080 *lenp = 0; 2192 *lenp = 0;
2081 return 0; 2193 return 0;
2082 } 2194 }
@@ -2088,89 +2200,69 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
2088 if (!conv) 2200 if (!conv)
2089 conv = do_proc_dointvec_conv; 2201 conv = do_proc_dointvec_conv;
2090 2202
2203 if (write) {
2204 if (left > PAGE_SIZE - 1)
2205 left = PAGE_SIZE - 1;
2206 page = __get_free_page(GFP_TEMPORARY);
2207 kbuf = (char *) page;
2208 if (!kbuf)
2209 return -ENOMEM;
2210 if (copy_from_user(kbuf, buffer, left)) {
2211 err = -EFAULT;
2212 goto free;
2213 }
2214 kbuf[left] = 0;
2215 }
2216
2091 for (; left && vleft--; i++, first=0) { 2217 for (; left && vleft--; i++, first=0) {
2092 if (write) { 2218