diff options
Diffstat (limited to 'kernel/sysctl.c')
| -rw-r--r-- | kernel/sysctl.c | 588 |
1 files changed, 424 insertions, 164 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8686b0f5fc12..4c93486b45d1 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -52,6 +52,7 @@ | |||
| 52 | #include <linux/slow-work.h> | 52 | #include <linux/slow-work.h> |
| 53 | #include <linux/perf_event.h> | 53 | #include <linux/perf_event.h> |
| 54 | #include <linux/kprobes.h> | 54 | #include <linux/kprobes.h> |
| 55 | #include <linux/pipe_fs_i.h> | ||
| 55 | 56 | ||
| 56 | #include <asm/uaccess.h> | 57 | #include <asm/uaccess.h> |
| 57 | #include <asm/processor.h> | 58 | #include <asm/processor.h> |
| @@ -163,6 +164,27 @@ static int proc_taint(struct ctl_table *table, int write, | |||
| 163 | void __user *buffer, size_t *lenp, loff_t *ppos); | 164 | void __user *buffer, size_t *lenp, loff_t *ppos); |
| 164 | #endif | 165 | #endif |
| 165 | 166 | ||
| 167 | #ifdef CONFIG_MAGIC_SYSRQ | ||
| 168 | static int __sysrq_enabled; /* Note: sysrq code ises it's own private copy */ | ||
| 169 | |||
| 170 | static int sysrq_sysctl_handler(ctl_table *table, int write, | ||
| 171 | void __user *buffer, size_t *lenp, | ||
| 172 | loff_t *ppos) | ||
| 173 | { | ||
| 174 | int error; | ||
| 175 | |||
| 176 | error = proc_dointvec(table, write, buffer, lenp, ppos); | ||
| 177 | if (error) | ||
| 178 | return error; | ||
| 179 | |||
| 180 | if (write) | ||
| 181 | sysrq_toggle_support(__sysrq_enabled); | ||
| 182 | |||
| 183 | return 0; | ||
| 184 | } | ||
| 185 | |||
| 186 | #endif | ||
| 187 | |||
| 166 | static struct ctl_table root_table[]; | 188 | static struct ctl_table root_table[]; |
| 167 | static struct ctl_table_root sysctl_table_root; | 189 | static struct ctl_table_root sysctl_table_root; |
| 168 | static struct ctl_table_header root_table_header = { | 190 | static struct ctl_table_header root_table_header = { |
| @@ -567,7 +589,7 @@ static struct ctl_table kern_table[] = { | |||
| 567 | .data = &__sysrq_enabled, | 589 | .data = &__sysrq_enabled, |
| 568 | .maxlen = sizeof (int), | 590 | .maxlen = sizeof (int), |
| 569 | .mode = 0644, | 591 | .mode = 0644, |
| 570 | .proc_handler = proc_dointvec, | 592 | .proc_handler = sysrq_sysctl_handler, |
| 571 | }, | 593 | }, |
| 572 | #endif | 594 | #endif |
| 573 | #ifdef CONFIG_PROC_SYSCTL | 595 | #ifdef CONFIG_PROC_SYSCTL |
| @@ -621,7 +643,7 @@ static struct ctl_table kern_table[] = { | |||
| 621 | #endif | 643 | #endif |
| 622 | { | 644 | { |
| 623 | .procname = "userprocess_debug", | 645 | .procname = "userprocess_debug", |
| 624 | .data = &sysctl_userprocess_debug, | 646 | .data = &show_unhandled_signals, |
| 625 | .maxlen = sizeof(int), | 647 | .maxlen = sizeof(int), |
| 626 | .mode = 0644, | 648 | .mode = 0644, |
| 627 | .proc_handler = proc_dointvec, | 649 | .proc_handler = proc_dointvec, |
| @@ -1423,6 +1445,14 @@ static struct ctl_table fs_table[] = { | |||
| 1423 | .child = binfmt_misc_table, | 1445 | .child = binfmt_misc_table, |
| 1424 | }, | 1446 | }, |
| 1425 | #endif | 1447 | #endif |
| 1448 | { | ||
| 1449 | .procname = "pipe-max-pages", | ||
| 1450 | .data = &pipe_max_pages, | ||
| 1451 | .maxlen = sizeof(int), | ||
| 1452 | .mode = 0644, | ||
| 1453 | .proc_handler = &proc_dointvec_minmax, | ||
| 1454 | .extra1 = &two, | ||
| 1455 | }, | ||
| 1426 | /* | 1456 | /* |
| 1427 | * NOTE: do not add new entries to this table unless you have read | 1457 | * NOTE: do not add new entries to this table unless you have read |
| 1428 | * Documentation/sysctl/ctl_unnumbered.txt | 1458 | * Documentation/sysctl/ctl_unnumbered.txt |
| @@ -1431,7 +1461,8 @@ static struct ctl_table fs_table[] = { | |||
| 1431 | }; | 1461 | }; |
| 1432 | 1462 | ||
| 1433 | static struct ctl_table debug_table[] = { | 1463 | static struct ctl_table debug_table[] = { |
| 1434 | #if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) | 1464 | #if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || \ |
| 1465 | defined(CONFIG_S390) | ||
| 1435 | { | 1466 | { |
| 1436 | .procname = "exception-trace", | 1467 | .procname = "exception-trace", |
| 1437 | .data = &show_unhandled_signals, | 1468 | .data = &show_unhandled_signals, |
| @@ -2040,8 +2071,132 @@ int proc_dostring(struct ctl_table *table, int write, | |||
| 2040 | buffer, lenp, ppos); | 2071 | buffer, lenp, ppos); |
| 2041 | } | 2072 | } |
| 2042 | 2073 | ||
| 2074 | static size_t proc_skip_spaces(char **buf) | ||
| 2075 | { | ||
| 2076 | size_t ret; | ||
| 2077 | char *tmp = skip_spaces(*buf); | ||
| 2078 | ret = tmp - *buf; | ||
| 2079 | *buf = tmp; | ||
| 2080 | return ret; | ||
| 2081 | } | ||
| 2082 | |||
| 2083 | static void proc_skip_char(char **buf, size_t *size, const char v) | ||
| 2084 | { | ||
| 2085 | while (*size) { | ||
| 2086 | if (**buf != v) | ||
| 2087 | break; | ||
| 2088 | (*size)--; | ||
| 2089 | (*buf)++; | ||
| 2090 | } | ||
| 2091 | } | ||
| 2092 | |||
| 2093 | #define TMPBUFLEN 22 | ||
| 2094 | /** | ||
| 2095 | * proc_get_long - reads an ASCII formatted integer from a user buffer | ||
| 2096 | * | ||
| 2097 | * @buf: a kernel buffer | ||
| 2098 | * @size: size of the kernel buffer | ||
| 2099 | * @val: this is where the number will be stored | ||
| 2100 | * @neg: set to %TRUE if number is negative | ||
| 2101 | * @perm_tr: a vector which contains the allowed trailers | ||
| 2102 | * @perm_tr_len: size of the perm_tr vector | ||
| 2103 | * @tr: pointer to store the trailer character | ||
| 2104 | * | ||
| 2105 | * In case of success %0 is returned and @buf and @size are updated with | ||
| 2106 | * the amount of bytes read. If @tr is non-NULL and a trailing | ||
| 2107 | * character exists (size is non-zero after returning from this | ||
| 2108 | * function), @tr is updated with the trailing character. | ||
| 2109 | */ | ||
| 2110 | static int proc_get_long(char **buf, size_t *size, | ||
| 2111 | unsigned long *val, bool *neg, | ||
| 2112 | const char *perm_tr, unsigned perm_tr_len, char *tr) | ||
| 2113 | { | ||
| 2114 | int len; | ||
| 2115 | char *p, tmp[TMPBUFLEN]; | ||
| 2116 | |||
| 2117 | if (!*size) | ||
| 2118 | return -EINVAL; | ||
| 2119 | |||
| 2120 | len = *size; | ||
| 2121 | if (len > TMPBUFLEN - 1) | ||
| 2122 | len = TMPBUFLEN - 1; | ||
| 2123 | |||
| 2124 | memcpy(tmp, *buf, len); | ||
| 2125 | |||
| 2126 | tmp[len] = 0; | ||
| 2127 | p = tmp; | ||
| 2128 | if (*p == '-' && *size > 1) { | ||
| 2129 | *neg = true; | ||
| 2130 | p++; | ||
| 2131 | } else | ||
| 2132 | *neg = false; | ||
| 2133 | if (!isdigit(*p)) | ||
| 2134 | return -EINVAL; | ||
| 2135 | |||
| 2136 | *val = simple_strtoul(p, &p, 0); | ||
| 2137 | |||
| 2138 | len = p - tmp; | ||
| 2139 | |||
| 2140 | /* We don't know if the next char is whitespace thus we may accept | ||
| 2141 | * invalid integers (e.g. 1234...a) or two integers instead of one | ||
| 2142 | * (e.g. 123...1). So lets not allow such large numbers. */ | ||
| 2143 | if (len == TMPBUFLEN - 1) | ||
| 2144 | return -EINVAL; | ||
| 2145 | |||
| 2146 | if (len < *size && perm_tr_len && !memchr(perm_tr, *p, perm_tr_len)) | ||
| 2147 | return -EINVAL; | ||
| 2148 | |||
| 2149 | if (tr && (len < *size)) | ||
| 2150 | *tr = *p; | ||
| 2151 | |||
| 2152 | *buf += len; | ||
| 2153 | *size -= len; | ||
| 2154 | |||
| 2155 | return 0; | ||
| 2156 | } | ||
| 2157 | |||
| 2158 | /** | ||
| 2159 | * proc_put_long - converts an integer to a decimal ASCII formatted string | ||
| 2160 | * | ||
| 2161 | * @buf: the user buffer | ||
| 2162 | * @size: the size of the user buffer | ||
| 2163 | * @val: the integer to be converted | ||
| 2164 | * @neg: sign of the number, %TRUE for negative | ||
| 2165 | * | ||
| 2166 | * In case of success %0 is returned and @buf and @size are updated with | ||
| 2167 | * the amount of bytes written. | ||
| 2168 | */ | ||
| 2169 | static int proc_put_long(void __user **buf, size_t *size, unsigned long val, | ||
| 2170 | bool neg) | ||
| 2171 | { | ||
| 2172 | int len; | ||
| 2173 | char tmp[TMPBUFLEN], *p = tmp; | ||
| 2174 | |||
| 2175 | sprintf(p, "%s%lu", neg ? "-" : "", val); | ||
| 2176 | len = strlen(tmp); | ||
| 2177 | if (len > *size) | ||
| 2178 | len = *size; | ||
| 2179 | if (copy_to_user(*buf, tmp, len)) | ||
| 2180 | return -EFAULT; | ||
| 2181 | *size -= len; | ||
| 2182 | *buf += len; | ||
| 2183 | return 0; | ||
| 2184 | } | ||
| 2185 | #undef TMPBUFLEN | ||
| 2186 | |||
| 2187 | static int proc_put_char(void __user **buf, size_t *size, char c) | ||
| 2188 | { | ||
| 2189 | if (*size) { | ||
| 2190 | char __user **buffer = (char __user **)buf; | ||
| 2191 | if (put_user(c, *buffer)) | ||
| 2192 | return -EFAULT; | ||
| 2193 | (*size)--, (*buffer)++; | ||
| 2194 | *buf = *buffer; | ||
| 2195 | } | ||
| 2196 | return 0; | ||
| 2197 | } | ||
| 2043 | 2198 | ||
| 2044 | static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, | 2199 | static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, |
| 2045 | int *valp, | 2200 | int *valp, |
| 2046 | int write, void *data) | 2201 | int write, void *data) |
| 2047 | { | 2202 | { |
| @@ -2050,33 +2205,31 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, | |||
| 2050 | } else { | 2205 | } else { |
| 2051 | int val = *valp; | 2206 | int val = *valp; |
| 2052 | if (val < 0) { | 2207 | if (val < 0) { |
| 2053 | *negp = -1; | 2208 | *negp = true; |
| 2054 | *lvalp = (unsigned long)-val; | 2209 | *lvalp = (unsigned long)-val; |
| 2055 | } else { | 2210 | } else { |
| 2056 | *negp = 0; | 2211 | *negp = false; |
| 2057 | *lvalp = (unsigned long)val; | 2212 | *lvalp = (unsigned long)val; |
| 2058 | } | 2213 | } |
| 2059 | } | 2214 | } |
| 2060 | return 0; | 2215 | return 0; |
| 2061 | } | 2216 | } |
| 2062 | 2217 | ||
| 2218 | static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; | ||
| 2219 | |||
| 2063 | static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, | 2220 | static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, |
| 2064 | int write, void __user *buffer, | 2221 | int write, void __user *buffer, |
| 2065 | size_t *lenp, loff_t *ppos, | 2222 | size_t *lenp, loff_t *ppos, |
| 2066 | int (*conv)(int *negp, unsigned long *lvalp, int *valp, | 2223 | int (*conv)(bool *negp, unsigned long *lvalp, int *valp, |
| 2067 | int write, void *data), | 2224 | int write, void *data), |
| 2068 | void *data) | 2225 | void *data) |
| 2069 | { | 2226 | { |
| 2070 | #define TMPBUFLEN 21 | 2227 | int *i, vleft, first = 1, err = 0; |
| 2071 | int *i, vleft, first = 1, neg; | 2228 | unsigned long page = 0; |
| 2072 | unsigned long lval; | 2229 | size_t left; |
| 2073 | size_t left, len; | 2230 | char *kbuf; |
| 2074 | 2231 | ||
| 2075 | char buf[TMPBUFLEN], *p; | 2232 | if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) { |
| 2076 | char __user *s = buffer; | ||
| 2077 | |||
| 2078 | if (!tbl_data || !table->maxlen || !*lenp || | ||
| 2079 | (*ppos && !write)) { | ||
| 2080 | *lenp = 0; | 2233 | *lenp = 0; |
| 2081 | return 0; | 2234 | return 0; |
| 2082 | } | 2235 | } |
| @@ -2088,89 +2241,69 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, | |||
| 2088 | if (!conv) | 2241 | if (!conv) |
| 2089 | conv = do_proc_dointvec_conv; | 2242 | conv = do_proc_dointvec_conv; |
| 2090 | 2243 | ||
| 2244 | if (write) { | ||
| 2245 | if (left > PAGE_SIZE - 1) | ||
| 2246 | left = PAGE_SIZE - 1; | ||
| 2247 | page = __get_free_page(GFP_TEMPORARY); | ||
| 2248 | kbuf = (char *) page; | ||
| 2249 | if (!kbuf) | ||
| 2250 | return -ENOMEM; | ||
| 2251 | if (copy_from_user(kbuf, buffer, left)) { | ||
| 2252 | err = -EFAULT; | ||
| 2253 | goto free; | ||
| 2254 | } | ||
| 2255 | kbuf[left] = 0; | ||
| 2256 | } | ||
| 2257 | |||
| 2091 | for (; left && vleft--; i++, first=0) { | 2258 | for (; left && vleft--; i++, first=0) { |
| 2092 | if (write) { | 2259 | unsigned long lval; |
| 2093 | while (left) { | 2260 | 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 | 2261 | ||
| 2119 | lval = simple_strtoul(p, &p, 0); | 2262 | if (write) { |
| 2263 | left -= proc_skip_spaces(&kbuf); | ||
| 2120 | 2264 | ||
| 2121 | len = p-buf; | 2265 | err = proc_get_long(&kbuf, &left, &lval, &neg, |
| 2122 | if ((len < left) && *p && !isspace(*p)) | 2266 | proc_wspace_sep, |
| 2267 | sizeof(proc_wspace_sep), NULL); | ||
| 2268 | if (err) | ||
| 2123 | break; | 2269 | break; |
| 2124 | s += len; | 2270 | if (conv(&neg, &lval, i, 1, data)) { |
| 2125 | left -= len; | 2271 | err = -EINVAL; |
| 2126 | |||
| 2127 | if (conv(&neg, &lval, i, 1, data)) | ||
| 2128 | break; | 2272 | break; |
| 2273 | } | ||
| 2129 | } else { | 2274 | } else { |
| 2130 | p = buf; | 2275 | if (conv(&neg, &lval, i, 0, data)) { |
| 2276 | err = -EINVAL; | ||
| 2277 | break; | ||
| 2278 | } | ||
| 2131 | if (!first) | 2279 | if (!first) |
| 2132 | *p++ = '\t'; | 2280 | err = proc_put_char(&buffer, &left, '\t'); |
| 2133 | 2281 | if (err) | |
| 2134 | if (conv(&neg, &lval, i, 0, data)) | 2282 | break; |
| 2283 | err = proc_put_long(&buffer, &left, lval, neg); | ||
| 2284 | if (err) | ||
| 2135 | break; | 2285 | 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 | } | 2286 | } |
| 2146 | } | 2287 | } |
| 2147 | 2288 | ||
| 2148 | if (!write && !first && left) { | 2289 | if (!write && !first && left && !err) |
| 2149 | if(put_user('\n', s)) | 2290 | err = proc_put_char(&buffer, &left, '\n'); |
| 2150 | return -EFAULT; | 2291 | if (write && !err) |
| 2151 | left--, s++; | 2292 | left -= proc_skip_spaces(&kbuf); |
| 2152 | } | 2293 | free: |
| 2153 | if (write) { | 2294 | if (write) { |
| 2154 | while (left) { | 2295 | free_page(page); |
| 2155 | char c; | 2296 | if (first) |
| 2156 | if (get_user(c, s++)) | 2297 | return err ? : -EINVAL; |
| 2157 | return -EFAULT; | ||
| 2158 | if (!isspace(c)) | ||
| 2159 | break; | ||
| 2160 | left--; | ||
| 2161 | } | ||
| 2162 | } | 2298 | } |
| 2163 | if (write && first) | ||
| 2164 | return -EINVAL; | ||
| 2165 | *lenp -= left; | 2299 | *lenp -= left; |
| 2166 | *ppos += *lenp; | 2300 | *ppos += *lenp; |
| 2167 | return 0; | 2301 | return err; |
| 2168 | #undef TMPBUFLEN | ||
| 2169 | } | 2302 | } |
| 2170 | 2303 | ||
| 2171 | static int do_proc_dointvec(struct ctl_table *table, int write, | 2304 | static int do_proc_dointvec(struct ctl_table *table, int write, |
| 2172 | void __user *buffer, size_t *lenp, loff_t *ppos, | 2305 | void __user *buffer, size_t *lenp, loff_t *ppos, |
| 2173 | int (*conv)(int *negp, unsigned long *lvalp, int *valp, | 2306 | int (*conv)(bool *negp, unsigned long *lvalp, int *valp, |
| 2174 | int write, void *data), | 2307 | int write, void *data), |
| 2175 | void *data) | 2308 | void *data) |
| 2176 | { | 2309 | { |
| @@ -2238,8 +2371,8 @@ struct do_proc_dointvec_minmax_conv_param { | |||
| 2238 | int *max; | 2371 | int *max; |
| 2239 | }; | 2372 | }; |
| 2240 | 2373 | ||
| 2241 | static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, | 2374 | static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp, |
| 2242 | int *valp, | 2375 | int *valp, |
| 2243 | int write, void *data) | 2376 | int write, void *data) |
| 2244 | { | 2377 | { |
| 2245 | struct do_proc_dointvec_minmax_conv_param *param = data; | 2378 | struct do_proc_dointvec_minmax_conv_param *param = data; |
| @@ -2252,10 +2385,10 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, | |||
| 2252 | } else { | 2385 | } else { |
| 2253 | int val = *valp; | 2386 | int val = *valp; |
| 2254 | if (val < 0) { | 2387 | if (val < 0) { |
| 2255 | *negp = -1; | 2388 | *negp = true; |
| 2256 | *lvalp = (unsigned long)-val; | 2389 | *lvalp = (unsigned long)-val; |
| 2257 | } else { | 2390 | } else { |
| 2258 | *negp = 0; | 2391 | *negp = false; |
| 2259 | *lvalp = (unsigned long)val; | 2392 | *lvalp = (unsigned long)val; |
| 2260 | } | 2393 | } |
| 2261 | } | 2394 | } |
| @@ -2295,102 +2428,78 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int | |||
| 2295 | unsigned long convmul, | 2428 | unsigned long convmul, |
| 2296 | unsigned long convdiv) | 2429 | unsigned long convdiv) |
| 2297 | { | 2430 | { |
| 2298 | #define TMPBUFLEN 21 | 2431 | unsigned long *i, *min, *max; |
| 2299 | unsigned long *i, *min, *max, val; | 2432 | int vleft, first = 1, err = 0; |
| 2300 | int vleft, first=1, neg; | 2433 | unsigned long page = 0; |
| 2301 | size_t len, left; | 2434 | size_t left; |
| 2302 | char buf[TMPBUFLEN], *p; | 2435 | char *kbuf; |
| 2303 | char __user *s = buffer; | 2436 | |
| 2304 | 2437 | if (!data || !table->maxlen || !*lenp || (*ppos && !write)) { | |
| 2305 | if (!data || !table->maxlen || !*lenp || | ||
| 2306 | (*ppos && !write)) { | ||
| 2307 | *lenp = 0; | 2438 | *lenp = 0; |
| 2308 | return 0; | 2439 | return 0; |
| 2309 | } | 2440 | } |
| 2310 | 2441 | ||
| 2311 | i = (unsigned long *) data; | 2442 | i = (unsigned long *) data; |
| 2312 | min = (unsigned long *) table->extra1; | 2443 | min = (unsigned long *) table->extra1; |
| 2313 | max = (unsigned long *) table->extra2; | 2444 | max = (unsigned long *) table->extra2; |
| 2314 | vleft = table->maxlen / sizeof(unsigned long); | 2445 | vleft = table->maxlen / sizeof(unsigned long); |
| 2315 | left = *lenp; | 2446 | left = *lenp; |
| 2316 | 2447 | ||
| 2448 | if (write) { | ||
| 2449 | if (left > PAGE_SIZE - 1) | ||
| 2450 | left = PAGE_SIZE - 1; | ||
| 2451 | page = __get_free_page(GFP_TEMPORARY); | ||
| 2452 | kbuf = (char *) page; | ||
| 2453 | if (!kbuf) | ||
| 2454 | return -ENOMEM; | ||
| 2455 | if (copy_from_user(kbuf, buffer, left)) { | ||
| 2456 | err = -EFAULT; | ||
| 2457 | goto free; | ||
| 2458 | } | ||
| 2459 | kbuf[left] = 0; | ||
| 2460 | } | ||
| 2461 | |||
| 2317 | for (; left && vleft--; i++, min++, max++, first=0) { | 2462 | for (; left && vleft--; i++, min++, max++, first=0) { |
| 2463 | unsigned long val; | ||
| 2464 | |||
| 2318 | if (write) { | 2465 | if (write) { |
| 2319 | while (left) { | 2466 | bool neg; |
| 2320 | char c; | 2467 | |
| 2321 | if (get_user(c, s)) | 2468 | left -= proc_skip_spaces(&kbuf); |
| 2322 | return -EFAULT; | 2469 | |
| 2323 | if (!isspace(c)) | 2470 | err = proc_get_long(&kbuf, &left, &val, &neg, |
| 2324 | break; | 2471 | proc_wspace_sep, |
| 2325 | left--; | 2472 | sizeof(proc_wspace_sep), NULL); |
| 2326 | s++; | 2473 | 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; | 2474 | break; |
| 2348 | if (neg) | 2475 | if (neg) |
| 2349 | val = -val; | ||
| 2350 | s += len; | ||
| 2351 | left -= len; | ||
| 2352 | |||
| 2353 | if(neg) | ||
| 2354 | continue; | 2476 | continue; |
| 2355 | if ((min && val < *min) || (max && val > *max)) | 2477 | if ((min && val < *min) || (max && val > *max)) |
| 2356 | continue; | 2478 | continue; |
| 2357 | *i = val; | 2479 | *i = val; |
| 2358 | } else { | 2480 | } else { |
| 2359 | p = buf; | 2481 | val = convdiv * (*i) / convmul; |
| 2360 | if (!first) | 2482 | if (!first) |
| 2361 | *p++ = '\t'; | 2483 | err = proc_put_char(&buffer, &left, '\t'); |
| 2362 | sprintf(p, "%lu", convdiv * (*i) / convmul); | 2484 | err = proc_put_long(&buffer, &left, val, false); |
| 2363 | len = strlen(buf); | 2485 | if (err) |
| 2364 | if (len > left) | 2486 | break; |
| 2365 | len = left; | ||
| 2366 | if(copy_to_user(s, buf, len)) | ||
| 2367 | return -EFAULT; | ||
| 2368 | left -= len; | ||
| 2369 | s += len; | ||
| 2370 | } | 2487 | } |
| 2371 | } | 2488 | } |
| 2372 | 2489 | ||
| 2373 | if (!write && !first && left) { | 2490 | if (!write && !first && left && !err) |
| 2374 | if(put_user('\n', s)) | 2491 | err = proc_put_char(&buffer, &left, '\n'); |
| 2375 | return -EFAULT; | 2492 | if (write && !err) |
| 2376 | left--, s++; | 2493 | left -= proc_skip_spaces(&kbuf); |
| 2377 | } | 2494 | free: |
| 2378 | if (write) { | 2495 | if (write) { |
| 2379 | while (left) { | 2496 | free_page(page); |
| 2380 | char c; | 2497 | if (first) |
| 2381 | if (get_user(c, s++)) | 2498 | return err ? : -EINVAL; |
| 2382 | return -EFAULT; | ||
| 2383 | if (!isspace(c)) | ||
| 2384 | break; | ||
| 2385 | left--; | ||
| 2386 | } | ||
| 2387 | } | 2499 | } |
| 2388 | if (write && first) | ||
| 2389 | return -EINVAL; | ||
| 2390 | *lenp -= left; | 2500 | *lenp -= left; |
| 2391 | *ppos += *lenp; | 2501 | *ppos += *lenp; |
| 2392 | return 0; | 2502 | return err; |
| 2393 | #undef TMPBUFLEN | ||
| 2394 | } | 2503 | } |
| 2395 | 2504 | ||
| 2396 | static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, | 2505 | static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, |
| @@ -2451,7 +2560,7 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write, | |||
| 2451 | } | 2560 | } |
| 2452 | 2561 | ||
| 2453 | 2562 | ||
| 2454 | static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | 2563 | static int do_proc_dointvec_jiffies_conv(bool *negp, unsigned long *lvalp, |
| 2455 | int *valp, | 2564 | int *valp, |
| 2456 | int write, void *data) | 2565 | int write, void *data) |
| 2457 | { | 2566 | { |
| @@ -2463,10 +2572,10 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2463 | int val = *valp; | 2572 | int val = *valp; |
| 2464 | unsigned long lval; | 2573 | unsigned long lval; |
| 2465 | if (val < 0) { | 2574 | if (val < 0) { |
| 2466 | *negp = -1; | 2575 | *negp = true; |
| 2467 | lval = (unsigned long)-val; | 2576 | lval = (unsigned long)-val; |
| 2468 | } else { | 2577 | } else { |
| 2469 | *negp = 0; | 2578 | *negp = false; |
| 2470 | lval = (unsigned long)val; | 2579 | lval = (unsigned long)val; |
| 2471 | } | 2580 | } |
| 2472 | *lvalp = lval / HZ; | 2581 | *lvalp = lval / HZ; |
| @@ -2474,7 +2583,7 @@ static int do_proc_dointvec_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2474 | return 0; | 2583 | return 0; |
| 2475 | } | 2584 | } |
| 2476 | 2585 | ||
| 2477 | static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | 2586 | static int do_proc_dointvec_userhz_jiffies_conv(bool *negp, unsigned long *lvalp, |
| 2478 | int *valp, | 2587 | int *valp, |
| 2479 | int write, void *data) | 2588 | int write, void *data) |
| 2480 | { | 2589 | { |
| @@ -2486,10 +2595,10 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2486 | int val = *valp; | 2595 | int val = *valp; |
| 2487 | unsigned long lval; | 2596 | unsigned long lval; |
| 2488 | if (val < 0) { | 2597 | if (val < 0) { |
| 2489 | *negp = -1; | 2598 | *negp = true; |
| 2490 | lval = (unsigned long)-val; | 2599 | lval = (unsigned long)-val; |
| 2491 | } else { | 2600 | } else { |
| 2492 | *negp = 0; | 2601 | *negp = false; |
| 2493 | lval = (unsigned long)val; | 2602 | lval = (unsigned long)val; |
| 2494 | } | 2603 | } |
| 2495 | *lvalp = jiffies_to_clock_t(lval); | 2604 | *lvalp = jiffies_to_clock_t(lval); |
| @@ -2497,7 +2606,7 @@ static int do_proc_dointvec_userhz_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2497 | return 0; | 2606 | return 0; |
| 2498 | } | 2607 | } |
| 2499 | 2608 | ||
| 2500 | static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, | 2609 | static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp, |
| 2501 | int *valp, | 2610 | int *valp, |
| 2502 | int write, void *data) | 2611 | int write, void *data) |
| 2503 | { | 2612 | { |
| @@ -2507,10 +2616,10 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, | |||
| 2507 | int val = *valp; | 2616 | int val = *valp; |
| 2508 | unsigned long lval; | 2617 | unsigned long lval; |
| 2509 | if (val < 0) { | 2618 | if (val < 0) { |
| 2510 | *negp = -1; | 2619 | *negp = true; |
| 2511 | lval = (unsigned long)-val; | 2620 | lval = (unsigned long)-val; |
| 2512 | } else { | 2621 | } else { |
| 2513 | *negp = 0; | 2622 | *negp = false; |
| 2514 | lval = (unsigned long)val; | 2623 | lval = (unsigned long)val; |
| 2515 | } | 2624 | } |
| 2516 | *lvalp = jiffies_to_msecs(lval); | 2625 | *lvalp = jiffies_to_msecs(lval); |
| @@ -2607,6 +2716,157 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, | |||
| 2607 | return 0; | 2716 | return 0; |
| 2608 | } | 2717 | } |
| 2609 | 2718 | ||
| 2719 | /** | ||
| 2720 | * proc_do_large_bitmap - read/write from/to a large bitmap | ||
| 2721 | * @table: the sysctl table | ||
| 2722 | * @write: %TRUE if this is a write to the sysctl file | ||
| 2723 | * @buffer: the user buffer | ||
| 2724 | * @lenp: the size of the user buffer | ||
| 2725 | * @ppos: file position | ||
| 2726 | * | ||
| 2727 | * The bitmap is stored at table->data and the bitmap length (in bits) | ||
| 2728 | * in table->maxlen. | ||
| 2729 | * | ||
| 2730 | * We use a range comma separated format (e.g. 1,3-4,10-10) so that | ||
| 2731 | * large bitmaps may be represented in a compact manner. Writing into | ||
| 2732 | * the file will clear the bitmap then update it with the given input. | ||
| 2733 | * | ||
| 2734 | * Returns 0 on success. | ||
| 2735 | */ | ||
| 2736 | int proc_do_large_bitmap(struct ctl_table *table, int write, | ||
| 2737 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
| 2738 | { | ||
| 2739 | int err = 0; | ||
| 2740 | bool first = 1; | ||
| 2741 | size_t left = *lenp; | ||
| 2742 | unsigned long bitmap_len = table->maxlen; | ||
| 2743 | unsigned long *bitmap = (unsigned long *) table->data; | ||
| 2744 | unsigned long *tmp_bitmap = NULL; | ||
| 2745 | char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c; | ||
| 2746 | |||
| 2747 | if (!bitmap_len || !left || (*ppos && !write)) { | ||
| 2748 | *lenp = 0; | ||
| 2749 | return 0; | ||
| 2750 | } | ||
| 2751 | |||
| 2752 | if (write) { | ||
| 2753 | unsigned long page = 0; | ||
| 2754 | char *kbuf; | ||
| 2755 | |||
| 2756 | if (left > PAGE_SIZE - 1) | ||
| 2757 | left = PAGE_SIZE - 1; | ||
| 2758 | |||
| 2759 | page = __get_free_page(GFP_TEMPORARY); | ||
| 2760 | kbuf = (char *) page; | ||
| 2761 | if (!kbuf) | ||
| 2762 | return -ENOMEM; | ||
| 2763 | if (copy_from_user(kbuf, buffer, left)) { | ||
| 2764 | free_page(page); | ||
| 2765 | return -EFAULT; | ||
| 2766 | } | ||
| 2767 | kbuf[left] = 0; | ||
| 2768 | |||
| 2769 | tmp_bitmap = kzalloc(BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long), | ||
| 2770 | GFP_KERNEL); | ||
| 2771 | if (!tmp_bitmap) { | ||
| 2772 | free_page(page); | ||
| 2773 | return -ENOMEM; | ||
| 2774 | } | ||
| 2775 | proc_skip_char(&kbuf, &left, '\n'); | ||
| 2776 | while (!err && left) { | ||
| 2777 | unsigned long val_a, val_b; | ||
| 2778 | bool neg; | ||
| 2779 | |||
| 2780 | err = proc_get_long(&kbuf, &left, &val_a, &neg, tr_a, | ||
| 2781 | sizeof(tr_a), &c); | ||
| 2782 | if (err) | ||
| 2783 | break; | ||
| 2784 | if (val_a >= bitmap_len || neg) { | ||
| 2785 | err = -EINVAL; | ||
| 2786 | break; | ||
| 2787 | } | ||
| 2788 | |||
| 2789 | val_b = val_a; | ||
| 2790 | if (left) { | ||
| 2791 | kbuf++; | ||
| 2792 | left--; | ||
| 2793 | } | ||
| 2794 | |||
| 2795 | if (c == '-') { | ||
| 2796 | err = proc_get_long(&kbuf, &left, &val_b, | ||
| 2797 | &neg, tr_b, sizeof(tr_b), | ||
| 2798 | &c); | ||
| 2799 | if (err) | ||
| 2800 | break; | ||
| 2801 | if (val_b >= bitmap_len || neg || | ||
| 2802 | val_a > val_b) { | ||
| 2803 | err = -EINVAL; | ||
| 2804 | break; | ||
| 2805 | } | ||
| 2806 | if (left) { | ||
| 2807 | kbuf++; | ||
| 2808 | left--; | ||
| 2809 | } | ||
| 2810 | } | ||
| 2811 | |||
| 2812 | while (val_a <= val_b) | ||
| 2813 | set_bit(val_a++, tmp_bitmap); | ||
| 2814 | |||
| 2815 | first = 0; | ||
| 2816 | proc_skip_char(&kbuf, &left, '\n'); | ||
| 2817 | } | ||
| 2818 | free_page(page); | ||
| 2819 | } else { | ||
| 2820 | unsigned long bit_a, bit_b = 0; | ||
| 2821 | |||
| 2822 | while (left) { | ||
| 2823 | bit_a = find_next_bit(bitmap, bitmap_len, bit_b); | ||
| 2824 | if (bit_a >= bitmap_len) | ||
| 2825 | break; | ||
| 2826 | bit_b = find_next_zero_bit(bitmap, bitmap_len, | ||
| 2827 | bit_a + 1) - 1; | ||
| 2828 | |||
| 2829 | if (!first) { | ||
| 2830 | err = proc_put_char(&buffer, &left, ','); | ||
| 2831 | if (err) | ||
| 2832 | break; | ||
| 2833 | } | ||
| 2834 | err = proc_put_long(&buffer, &left, bit_a, false); | ||
| 2835 | if (err) | ||
| 2836 | break; | ||
| 2837 | if (bit_a != bit_b) { | ||
| 2838 | err = proc_put_char(&buffer, &left, '-'); | ||
| 2839 | if (err) | ||
| 2840 | break; | ||
| 2841 | err = proc_put_long(&buffer, &left, bit_b, false); | ||
| 2842 | if (err) | ||
| 2843 | break; | ||
| 2844 | } | ||
| 2845 | |||
| 2846 | first = 0; bit_b++; | ||
| 2847 | } | ||
| 2848 | if (!err) | ||
| 2849 | err = proc_put_char(&buffer, &left, '\n'); | ||
| 2850 | } | ||
| 2851 | |||
| 2852 | if (!err) { | ||
| 2853 | if (write) { | ||
| 2854 | if (*ppos) | ||
| 2855 | bitmap_or(bitmap, bitmap, tmp_bitmap, bitmap_len); | ||
| 2856 | else | ||
| 2857 | memcpy(bitmap, tmp_bitmap, | ||
| 2858 | BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long)); | ||
| 2859 | } | ||
| 2860 | kfree(tmp_bitmap); | ||
| 2861 | *lenp -= left; | ||
| 2862 | *ppos += *lenp; | ||
| 2863 | return 0; | ||
| 2864 | } else { | ||
| 2865 | kfree(tmp_bitmap); | ||
| 2866 | return err; | ||
| 2867 | } | ||
| 2868 | } | ||
| 2869 | |||
| 2610 | #else /* CONFIG_PROC_FS */ | 2870 | #else /* CONFIG_PROC_FS */ |
| 2611 | 2871 | ||
| 2612 | int proc_dostring(struct ctl_table *table, int write, | 2872 | int proc_dostring(struct ctl_table *table, int write, |
