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 unsigned long lval;
2093 while (left) { 2219 bool neg;
2094 char c;
2095 if (get_user(c, s))
2096 return -EFAULT;
2097 if (!isspace(c))
2098 break;
2099 left--;
2100 s++;
2101 }
2102 if (!left)
2103 break;
2104 neg = 0;
2105 len = left;
2106 if (len > sizeof(buf) - 1)
2107 len = sizeof(buf) - 1;
2108 if (copy_from_user(buf, s, len))
2109 return -EFAULT;
2110 buf[len] = 0;
2111 p = buf;
2112 if (*p == '-' && left > 1) {
2113 neg = 1;
2114 p++;
2115 }
2116 if (*p < '0' || *p > '9')
2117 break;
2118 2220
2119 lval = simple_strtoul(p, &p, 0); 2221 if (write) {
2222 left -= proc_skip_spaces(&kbuf);
2120 2223
2121 len = p-buf; 2224 err = proc_get_long(&kbuf, &left, &lval, &neg,
2122 if ((len < left) && *p && !isspace(*p)) 2225 proc_wspace_sep,
2226 sizeof(proc_wspace_sep), NULL);
2227 if (err)
2123 break; 2228 break;
2124 s += len; 2229 if (conv(&neg, &lval, i, 1, data)) {
2125 left -= len; 2230 err = -EINVAL;
2126
2127 if (conv(&neg, &lval, i, 1, data))
2128 break; 2231 break;
2232 }
2129 } else { 2233 } else {
2130 p = buf; 2234 if (conv(&neg, &lval, i, 0, data)) {
2235 err = -EINVAL;
2236 break;
2237 }
2131 if (!first) 2238 if (!first)
2132 *p++ = '\t'; 2239 err = proc_put_char(&buffer, &left, '\t');
2133 2240 if (err)
2134 if (conv(&neg, &lval, i, 0, data)) 2241 break;
2242 err = proc_put_long(&buffer, &left, lval, neg);
2243 if (err)
2135 break; 2244 break;
2136
2137 sprintf(p, "%s%lu", neg ? "-" : "", lval);
2138 len = strlen(buf);
2139 if (len > left)
2140 len = left;
2141 if(copy_to_user(s, buf, len))
2142 return -EFAULT;
2143 left -= len;
2144 s += len;
2145 } 2245 }
2146 } 2246 }
2147 2247
2148 if (!write && !first && left) { 2248 if (!write && !first && left && !err)
2149 if(put_user('\n', s)) 2249 err = proc_put_char(&buffer, &left, '\n');
2150 return -EFAULT; 2250 if (write && !err)
2151 left--, s++; 2251 left -= proc_skip_spaces(&kbuf);
2152 } 2252free:
2153 if (write) { 2253 if (write) {
2154 while (left) { 2254 free_page(page);
2155 char c; 2255 if (first)
2156 if (get_user(c, s++)) 2256 return err ? : -EINVAL;
2157 return -EFAULT;
2158 if (!isspace(c))
2159 break;
2160 left--;
2161 }
2162 } 2257 }
2163 if (write && first)
2164 return -EINVAL;
2165 *lenp -= left; 2258 *lenp -= left;
2166 *ppos += *lenp; 2259 *ppos += *lenp;
2167 return 0; 2260 return err;
2168#undef TMPBUFLEN
2169} 2261}
2170 2262
2171static int do_proc_dointvec(struct ctl_table *table, int write, 2263static int do_proc_dointvec(struct ctl_table *table, int write,
2172 void __user *buffer, size_t *lenp, loff_t *ppos, 2264 void __user *buffer, size_t *lenp, loff_t *ppos,
2173 int (*conv)(int *negp, unsigned long *lvalp, int *valp, 2265 int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
2174 int write, void *data), 2266 int write, void *data),
2175 void *data) 2267 void *data)
2176{ 2268{
@@ -2238,8 +2330,8 @@ struct do_proc_dointvec_minmax_conv_param {
2238 int *max; 2330 int *max;
2239}; 2331};
2240 2332
2241static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, 2333static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
2242 int *valp, 2334 int *valp,
2243 int write, void *data) 2335 int write, void *data)
2244{ 2336{
2245 struct do_proc_dointvec_minmax_conv_param *param = data; 2337 struct do_proc_dointvec_minmax_conv_param *param = data;
@@ -2252,10 +2344,10 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp,
2252 } else { 2344 } else {
2253 int val = *valp; 2345 int val = *valp;
2254 if (val < 0) { 2346 if (val < 0) {
2255 *negp = -1; 2347 *negp = true;
2256 *lvalp = (unsigned long)-val; 2348 *lvalp = (unsigned long)-val;
2257 } else { 2349 } else {
2258 *negp = 0; 2350 *negp = false;
2259 *lvalp = (unsigned long)val; 2351 *lvalp = (unsigned long)val;
2260 } 2352 }
2261 } 2353 }
@@ -2295,102 +2387,78 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
2295 unsigned long convmul, 2387 unsigned long convmul,
2296 unsigned long convdiv) 2388 unsigned long convdiv)
2297{ 2389{
2298#define TMPBUFLEN 21 2390 unsigned long *i, *min, *max;
2299 unsigned long *i, *min, *max, val; 2391 int vleft, first = 1, err = 0;
2300 int vleft, first=1, neg; 2392 unsigned long page = 0;
2301 size_t len, left; 2393 size_t left;
2302 char buf[TMPBUFLEN], *p; 2394 char *kbuf;
2303 char __user *s = buffer; 2395
2304 2396 if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
2305 if (!data || !table->maxlen || !*lenp ||
2306 (*ppos && !write)) {
2307 *lenp = 0; 2397 *lenp = 0;
2308 return 0; 2398 return 0;
2309 } 2399 }
2310 2400
2311 i = (unsigned long *) data; 2401 i = (unsigned long *) data;
2312 min = (unsigned long *) table->extra1; 2402 min = (unsigned long *) table->extra1;
2313 max = (unsigned long *) table->extra2; 2403 max = (unsigned long *) table->extra2;
2314 vleft = table->maxlen / sizeof(unsigned long); 2404 vleft = table->maxlen / sizeof(unsigned long);
2315 left = *lenp; 2405 left = *lenp;
2316 2406
2407 if (write) {
2408 if (left > PAGE_SIZE - 1)
2409 left = PAGE_SIZE - 1;
2410 page = __get_free_page(GFP_TEMPORARY);
2411 kbuf = (char *) page;
2412 if (!kbuf)
2413 return -ENOMEM;
2414 if (copy_from_user(kbuf, buffer, left)) {
2415 err = -EFAULT;
2416 goto free;
2417 }
2418 kbuf[left] = 0;
2419 }
2420
2317 for (; left && vleft--; i++, min++, max++, first=0) { 2421 for (; left && vleft--; i++, min++, max++, first=0) {
2422 unsigned long val;
2423
2318 if (write) { 2424 if (write) {
2319 while (left) { 2425 bool neg;
2320 char c; 2426
2321 if (get_user(c, s)) 2427 left -= proc_skip_spaces(&kbuf);
2322 return -EFAULT; 2428
2323 if (!isspace(c)) 2429 err = proc_get_long(&kbuf, &left, &val, &neg,
2324 break; 2430 proc_wspace_sep,
2325 left--; 2431 sizeof(proc_wspace_sep), NULL);
2326 s++; 2432 if (err)
2327 }
2328 if (!left)
2329 break;
2330 neg = 0;
2331 len = left;
2332 if (len > TMPBUFLEN-1)
2333 len = TMPBUFLEN-1;
2334 if (copy_from_user(buf, s, len))
2335 return -EFAULT;
2336 buf[len] = 0;
2337 p = buf;
2338 if (*p == '-' && left > 1) {
2339 neg = 1;
2340 p++;
2341 }
2342 if (*p < '0' || *p > '9')
2343 break;
2344 val = simple_strtoul(p, &p, 0) * convmul / convdiv ;
2345 len = p-buf;
2346 if ((len < left) && *p && !isspace(*p))
2347 break; 2433 break;
2348 if (neg) 2434 if (neg)
2349 val = -val;
2350 s += len;
2351 left -= len;
2352
2353 if(neg)
2354 continue; 2435 continue;
2355 if ((min && val < *min) || (max && val > *max)) 2436 if ((min && val < *min) || (max && val > *max))
2356 continue; 2437 continue;
2357 *i = val; 2438 *i = val;
2358 } else { 2439 } else {
2359 p = buf; 2440 val = convdiv * (*i) / convmul;
2360 if (!first) 2441 if (!first)
2361 *p++ = '\t'; 2442 err = proc_put_char(&buffer, &left, '\t');
2362 sprintf(p, "%lu", convdiv * (*i) / convmul); 2443 err = proc_put_long(&buffer, &left, val, false);
2363 len = strlen(buf); 2444 if (err)
2364 if (len > left) 2445 break;
2365 len = left;
2366 if(copy_to_user(s, buf, len))
2367 return -EFAULT;
2368 left -= len;
2369 s += len;
2370 } 2446 }
2371 } 2447 }
2372 2448
2373 if (!write && !first && left) { 2449 if (!write && !first && left && !err)
2374 if(put_user('\n', s)) 2450 err = proc_put_char(&buffer, &left, '\n');
2375 return -EFAULT; 2451 if (write && !err)
2376 left--, s++; 2452 left -= proc_skip_spaces(&kbuf);
2377 } 2453free:
2378 if (write) { 2454 if (write) {
2379 while (left) { 2455 free_page(page);
2380 char c; 2456 if (first)
2381 if (get_user(c, s++)) 2457 return err ? : -EINVAL;
2382 return -EFAULT;
2383 if (!isspace(c))
2384 break;
2385 left--;
2386 }
2387 } 2458 }
2388 if (write && first)
2389 return -EINVAL;
2390 *lenp -= left; 2459 *lenp -= left;
2391 *ppos += *lenp; 2460 *ppos += *lenp;
2392 return 0; 2461 return err;
2393#undef TMPBUFLEN
2394} 2462}
2395 2463
2396static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, 2464static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
@@ -2451,7 +2519,7 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
2451} 2519}
2452 2520
2453 2521
2454static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, 2522static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp,
2455 int *valp, 2523 int *valp,
2456 int write, void *data) 2524 int write, void *data)
2457{ 2525{
@@ -2463,10 +2531,10 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
2463 int val = *valp; 2531 int val = *valp;
2464 unsigned long lval; 2532 unsigned long lval;
2465 if (val < 0) { 2533 if (val < 0) {
2466 *negp = -1; 2534 *negp = true;
2467 lval = (unsigned long)-val; 2535 lval = (unsigned long)-val;
2468 } else { 2536 } else {
2469 *negp = 0; 2537 *negp = false;
2470 lval = (unsigned long)val; 2538 lval = (unsigned long)val;
2471 } 2539 }
2472 *lvalp = lval / HZ; 2540 *lvalp = lval / HZ;
@@ -2474,7 +2542,7 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp,
2474 return 0; 2542 return 0;
2475} 2543}
2476 2544
2477static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, 2545static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp,
2478 int *valp, 2546 int *valp,
2479 int write, void *data) 2547 int write, void *data)
2480{ 2548{
@@ -2486,10 +2554,10 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
2486 int val = *valp; 2554 int val = *valp;
2487 unsigned long lval; 2555 unsigned long lval;
2488 if (val < 0) { 2556 if (val < 0) {
2489 *negp = -1; 2557 *negp = true;
2490 lval = (unsigned long)-val; 2558 lval = (unsigned long)-val;
2491 } else { 2559 } else {
2492 *negp = 0; 2560 *negp = false;
2493 lval = (unsigned long)val; 2561 lval = (unsigned long)val;
2494 } 2562 }
2495 *lvalp = jiffies_to_clock_t(lval); 2563 *lvalp = jiffies_to_clock_t(lval);
@@ -2497,7 +2565,7 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp,
2497 return 0; 2565 return 0;
2498} 2566}
2499 2567
2500static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, 2568static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
2501 int *valp, 2569 int *valp,
2502 int write, void *data) 2570 int write, void *data)
2503{ 2571{
@@ -2507,10 +2575,10 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp,
2507 int val = *valp; 2575 int val = *valp;
2508 unsigned long lval; 2576 unsigned long lval;
2509 if (val < 0) { 2577 if (val < 0) {
2510 *negp = -1; 2578 *negp = true;
2511 lval = (unsigned long)-val; 2579 lval = (unsigned long)-val;
2512 } else { 2580 } else {
2513 *negp = 0; 2581 *negp = false;
2514 lval = (unsigned long)val; 2582 lval = (unsigned long)val;
2515 } 2583 }
2516 *lvalp = jiffies_to_msecs(lval); 2584 *lvalp = jiffies_to_msecs(lval);