aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/sysctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/sysctl.c')
-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);