diff options
Diffstat (limited to 'kernel/sysctl.c')
-rw-r--r-- | kernel/sysctl.c | 276 |
1 files changed, 219 insertions, 57 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 29116652dca8..cfc5295f1e82 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -43,9 +43,11 @@ | |||
43 | #include <linux/limits.h> | 43 | #include <linux/limits.h> |
44 | #include <linux/dcache.h> | 44 | #include <linux/dcache.h> |
45 | #include <linux/syscalls.h> | 45 | #include <linux/syscalls.h> |
46 | #include <linux/vmstat.h> | ||
46 | #include <linux/nfs_fs.h> | 47 | #include <linux/nfs_fs.h> |
47 | #include <linux/acpi.h> | 48 | #include <linux/acpi.h> |
48 | #include <linux/reboot.h> | 49 | #include <linux/reboot.h> |
50 | #include <linux/ftrace.h> | ||
49 | 51 | ||
50 | #include <asm/uaccess.h> | 52 | #include <asm/uaccess.h> |
51 | #include <asm/processor.h> | 53 | #include <asm/processor.h> |
@@ -78,21 +80,23 @@ extern int pid_max_min, pid_max_max; | |||
78 | extern int sysctl_drop_caches; | 80 | extern int sysctl_drop_caches; |
79 | extern int percpu_pagelist_fraction; | 81 | extern int percpu_pagelist_fraction; |
80 | extern int compat_log; | 82 | extern int compat_log; |
81 | extern int maps_protect; | ||
82 | extern int sysctl_stat_interval; | ||
83 | extern int latencytop_enabled; | 83 | extern int latencytop_enabled; |
84 | extern int sysctl_nr_open_min, sysctl_nr_open_max; | 84 | extern int sysctl_nr_open_min, sysctl_nr_open_max; |
85 | #ifdef CONFIG_RCU_TORTURE_TEST | ||
86 | extern int rcutorture_runnable; | ||
87 | #endif /* #ifdef CONFIG_RCU_TORTURE_TEST */ | ||
85 | 88 | ||
86 | /* Constants used for minimum and maximum */ | 89 | /* Constants used for minimum and maximum */ |
87 | #if defined(CONFIG_DETECT_SOFTLOCKUP) || defined(CONFIG_HIGHMEM) | 90 | #if defined(CONFIG_HIGHMEM) || defined(CONFIG_DETECT_SOFTLOCKUP) |
88 | static int one = 1; | 91 | static int one = 1; |
89 | #endif | 92 | #endif |
90 | 93 | ||
91 | #ifdef CONFIG_DETECT_SOFTLOCKUP | 94 | #ifdef CONFIG_DETECT_SOFTLOCKUP |
92 | static int sixty = 60; | 95 | static int sixty = 60; |
96 | static int neg_one = -1; | ||
93 | #endif | 97 | #endif |
94 | 98 | ||
95 | #ifdef CONFIG_MMU | 99 | #if defined(CONFIG_MMU) && defined(CONFIG_FILE_LOCKING) |
96 | static int two = 2; | 100 | static int two = 2; |
97 | #endif | 101 | #endif |
98 | 102 | ||
@@ -106,17 +110,15 @@ static int min_percpu_pagelist_fract = 8; | |||
106 | 110 | ||
107 | static int ngroups_max = NGROUPS_MAX; | 111 | static int ngroups_max = NGROUPS_MAX; |
108 | 112 | ||
109 | #ifdef CONFIG_KMOD | 113 | #ifdef CONFIG_MODULES |
110 | extern char modprobe_path[]; | 114 | extern char modprobe_path[]; |
111 | #endif | 115 | #endif |
112 | #ifdef CONFIG_CHR_DEV_SG | 116 | #ifdef CONFIG_CHR_DEV_SG |
113 | extern int sg_big_buff; | 117 | extern int sg_big_buff; |
114 | #endif | 118 | #endif |
115 | 119 | ||
116 | #ifdef __sparc__ | 120 | #ifdef CONFIG_SPARC |
117 | extern char reboot_command []; | 121 | #include <asm/system.h> |
118 | extern int stop_a_enabled; | ||
119 | extern int scons_pwroff; | ||
120 | #endif | 122 | #endif |
121 | 123 | ||
122 | #ifdef __hppa__ | 124 | #ifdef __hppa__ |
@@ -132,8 +134,6 @@ extern int sysctl_userprocess_debug; | |||
132 | extern int spin_retry; | 134 | extern int spin_retry; |
133 | #endif | 135 | #endif |
134 | 136 | ||
135 | extern int sysctl_hz_timer; | ||
136 | |||
137 | #ifdef CONFIG_BSD_PROCESS_ACCT | 137 | #ifdef CONFIG_BSD_PROCESS_ACCT |
138 | extern int acct_parm[]; | 138 | extern int acct_parm[]; |
139 | #endif | 139 | #endif |
@@ -156,13 +156,15 @@ static int proc_dointvec_taint(struct ctl_table *table, int write, struct file * | |||
156 | static struct ctl_table root_table[]; | 156 | static struct ctl_table root_table[]; |
157 | static struct ctl_table_root sysctl_table_root; | 157 | static struct ctl_table_root sysctl_table_root; |
158 | static struct ctl_table_header root_table_header = { | 158 | static struct ctl_table_header root_table_header = { |
159 | .count = 1, | ||
159 | .ctl_table = root_table, | 160 | .ctl_table = root_table, |
160 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list), | 161 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list), |
161 | .root = &sysctl_table_root, | 162 | .root = &sysctl_table_root, |
163 | .set = &sysctl_table_root.default_set, | ||
162 | }; | 164 | }; |
163 | static struct ctl_table_root sysctl_table_root = { | 165 | static struct ctl_table_root sysctl_table_root = { |
164 | .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), | 166 | .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), |
165 | .header_list = LIST_HEAD_INIT(root_table_header.ctl_entry), | 167 | .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), |
166 | }; | 168 | }; |
167 | 169 | ||
168 | static struct ctl_table kern_table[]; | 170 | static struct ctl_table kern_table[]; |
@@ -266,6 +268,14 @@ static struct ctl_table kern_table[] = { | |||
266 | }, | 268 | }, |
267 | { | 269 | { |
268 | .ctl_name = CTL_UNNUMBERED, | 270 | .ctl_name = CTL_UNNUMBERED, |
271 | .procname = "sched_shares_ratelimit", | ||
272 | .data = &sysctl_sched_shares_ratelimit, | ||
273 | .maxlen = sizeof(unsigned int), | ||
274 | .mode = 0644, | ||
275 | .proc_handler = &proc_dointvec, | ||
276 | }, | ||
277 | { | ||
278 | .ctl_name = CTL_UNNUMBERED, | ||
269 | .procname = "sched_child_runs_first", | 279 | .procname = "sched_child_runs_first", |
270 | .data = &sysctl_sched_child_runs_first, | 280 | .data = &sysctl_sched_child_runs_first, |
271 | .maxlen = sizeof(unsigned int), | 281 | .maxlen = sizeof(unsigned int), |
@@ -402,7 +412,7 @@ static struct ctl_table kern_table[] = { | |||
402 | .mode = 0644, | 412 | .mode = 0644, |
403 | .proc_handler = &proc_dointvec, | 413 | .proc_handler = &proc_dointvec, |
404 | }, | 414 | }, |
405 | #ifdef __sparc__ | 415 | #ifdef CONFIG_SPARC |
406 | { | 416 | { |
407 | .ctl_name = KERN_SPARC_REBOOT, | 417 | .ctl_name = KERN_SPARC_REBOOT, |
408 | .procname = "reboot-cmd", | 418 | .procname = "reboot-cmd", |
@@ -455,7 +465,17 @@ static struct ctl_table kern_table[] = { | |||
455 | .mode = 0644, | 465 | .mode = 0644, |
456 | .proc_handler = &proc_dointvec, | 466 | .proc_handler = &proc_dointvec, |
457 | }, | 467 | }, |
458 | #ifdef CONFIG_KMOD | 468 | #ifdef CONFIG_FTRACE |
469 | { | ||
470 | .ctl_name = CTL_UNNUMBERED, | ||
471 | .procname = "ftrace_enabled", | ||
472 | .data = &ftrace_enabled, | ||
473 | .maxlen = sizeof(int), | ||
474 | .mode = 0644, | ||
475 | .proc_handler = &ftrace_enable_sysctl, | ||
476 | }, | ||
477 | #endif | ||
478 | #ifdef CONFIG_MODULES | ||
459 | { | 479 | { |
460 | .ctl_name = KERN_MODPROBE, | 480 | .ctl_name = KERN_MODPROBE, |
461 | .procname = "modprobe", | 481 | .procname = "modprobe", |
@@ -563,16 +583,6 @@ static struct ctl_table kern_table[] = { | |||
563 | .proc_handler = &proc_dointvec, | 583 | .proc_handler = &proc_dointvec, |
564 | }, | 584 | }, |
565 | #endif | 585 | #endif |
566 | #ifdef CONFIG_NO_IDLE_HZ | ||
567 | { | ||
568 | .ctl_name = KERN_HZ_TIMER, | ||
569 | .procname = "hz_timer", | ||
570 | .data = &sysctl_hz_timer, | ||
571 | .maxlen = sizeof(int), | ||
572 | .mode = 0644, | ||
573 | .proc_handler = &proc_dointvec, | ||
574 | }, | ||
575 | #endif | ||
576 | { | 586 | { |
577 | .ctl_name = KERN_S390_USER_DEBUG_LOGGING, | 587 | .ctl_name = KERN_S390_USER_DEBUG_LOGGING, |
578 | .procname = "userprocess_debug", | 588 | .procname = "userprocess_debug", |
@@ -613,7 +623,7 @@ static struct ctl_table kern_table[] = { | |||
613 | { | 623 | { |
614 | .ctl_name = KERN_PRINTK_RATELIMIT, | 624 | .ctl_name = KERN_PRINTK_RATELIMIT, |
615 | .procname = "printk_ratelimit", | 625 | .procname = "printk_ratelimit", |
616 | .data = &printk_ratelimit_jiffies, | 626 | .data = &printk_ratelimit_state.interval, |
617 | .maxlen = sizeof(int), | 627 | .maxlen = sizeof(int), |
618 | .mode = 0644, | 628 | .mode = 0644, |
619 | .proc_handler = &proc_dointvec_jiffies, | 629 | .proc_handler = &proc_dointvec_jiffies, |
@@ -622,7 +632,7 @@ static struct ctl_table kern_table[] = { | |||
622 | { | 632 | { |
623 | .ctl_name = KERN_PRINTK_RATELIMIT_BURST, | 633 | .ctl_name = KERN_PRINTK_RATELIMIT_BURST, |
624 | .procname = "printk_ratelimit_burst", | 634 | .procname = "printk_ratelimit_burst", |
625 | .data = &printk_ratelimit_burst, | 635 | .data = &printk_ratelimit_state.burst, |
626 | .maxlen = sizeof(int), | 636 | .maxlen = sizeof(int), |
627 | .mode = 0644, | 637 | .mode = 0644, |
628 | .proc_handler = &proc_dointvec, | 638 | .proc_handler = &proc_dointvec, |
@@ -729,13 +739,24 @@ static struct ctl_table kern_table[] = { | |||
729 | #ifdef CONFIG_DETECT_SOFTLOCKUP | 739 | #ifdef CONFIG_DETECT_SOFTLOCKUP |
730 | { | 740 | { |
731 | .ctl_name = CTL_UNNUMBERED, | 741 | .ctl_name = CTL_UNNUMBERED, |
742 | .procname = "softlockup_panic", | ||
743 | .data = &softlockup_panic, | ||
744 | .maxlen = sizeof(int), | ||
745 | .mode = 0644, | ||
746 | .proc_handler = &proc_dointvec_minmax, | ||
747 | .strategy = &sysctl_intvec, | ||
748 | .extra1 = &zero, | ||
749 | .extra2 = &one, | ||
750 | }, | ||
751 | { | ||
752 | .ctl_name = CTL_UNNUMBERED, | ||
732 | .procname = "softlockup_thresh", | 753 | .procname = "softlockup_thresh", |
733 | .data = &softlockup_thresh, | 754 | .data = &softlockup_thresh, |
734 | .maxlen = sizeof(unsigned long), | 755 | .maxlen = sizeof(int), |
735 | .mode = 0644, | 756 | .mode = 0644, |
736 | .proc_handler = &proc_doulongvec_minmax, | 757 | .proc_handler = &proc_dointvec_minmax, |
737 | .strategy = &sysctl_intvec, | 758 | .strategy = &sysctl_intvec, |
738 | .extra1 = &one, | 759 | .extra1 = &neg_one, |
739 | .extra2 = &sixty, | 760 | .extra2 = &sixty, |
740 | }, | 761 | }, |
741 | { | 762 | { |
@@ -786,16 +807,6 @@ static struct ctl_table kern_table[] = { | |||
786 | .proc_handler = &proc_dointvec, | 807 | .proc_handler = &proc_dointvec, |
787 | }, | 808 | }, |
788 | #endif | 809 | #endif |
789 | #ifdef CONFIG_PROC_FS | ||
790 | { | ||
791 | .ctl_name = CTL_UNNUMBERED, | ||
792 | .procname = "maps_protect", | ||
793 | .data = &maps_protect, | ||
794 | .maxlen = sizeof(int), | ||
795 | .mode = 0644, | ||
796 | .proc_handler = &proc_dointvec, | ||
797 | }, | ||
798 | #endif | ||
799 | { | 810 | { |
800 | .ctl_name = CTL_UNNUMBERED, | 811 | .ctl_name = CTL_UNNUMBERED, |
801 | .procname = "poweroff_cmd", | 812 | .procname = "poweroff_cmd", |
@@ -813,6 +824,16 @@ static struct ctl_table kern_table[] = { | |||
813 | .child = key_sysctls, | 824 | .child = key_sysctls, |
814 | }, | 825 | }, |
815 | #endif | 826 | #endif |
827 | #ifdef CONFIG_RCU_TORTURE_TEST | ||
828 | { | ||
829 | .ctl_name = CTL_UNNUMBERED, | ||
830 | .procname = "rcutorture_runnable", | ||
831 | .data = &rcutorture_runnable, | ||
832 | .maxlen = sizeof(int), | ||
833 | .mode = 0644, | ||
834 | .proc_handler = &proc_dointvec, | ||
835 | }, | ||
836 | #endif | ||
816 | /* | 837 | /* |
817 | * NOTE: do not add new entries to this table unless you have read | 838 | * NOTE: do not add new entries to this table unless you have read |
818 | * Documentation/sysctl/ctl_unnumbered.txt | 839 | * Documentation/sysctl/ctl_unnumbered.txt |
@@ -927,7 +948,7 @@ static struct ctl_table vm_table[] = { | |||
927 | #ifdef CONFIG_HUGETLB_PAGE | 948 | #ifdef CONFIG_HUGETLB_PAGE |
928 | { | 949 | { |
929 | .procname = "nr_hugepages", | 950 | .procname = "nr_hugepages", |
930 | .data = &max_huge_pages, | 951 | .data = NULL, |
931 | .maxlen = sizeof(unsigned long), | 952 | .maxlen = sizeof(unsigned long), |
932 | .mode = 0644, | 953 | .mode = 0644, |
933 | .proc_handler = &hugetlb_sysctl_handler, | 954 | .proc_handler = &hugetlb_sysctl_handler, |
@@ -953,10 +974,12 @@ static struct ctl_table vm_table[] = { | |||
953 | { | 974 | { |
954 | .ctl_name = CTL_UNNUMBERED, | 975 | .ctl_name = CTL_UNNUMBERED, |
955 | .procname = "nr_overcommit_hugepages", | 976 | .procname = "nr_overcommit_hugepages", |
956 | .data = &sysctl_overcommit_huge_pages, | 977 | .data = NULL, |
957 | .maxlen = sizeof(sysctl_overcommit_huge_pages), | 978 | .maxlen = sizeof(unsigned long), |
958 | .mode = 0644, | 979 | .mode = 0644, |
959 | .proc_handler = &hugetlb_overcommit_handler, | 980 | .proc_handler = &hugetlb_overcommit_handler, |
981 | .extra1 = (void *)&hugetlb_zero, | ||
982 | .extra2 = (void *)&hugetlb_infinity, | ||
960 | }, | 983 | }, |
961 | #endif | 984 | #endif |
962 | { | 985 | { |
@@ -1225,6 +1248,7 @@ static struct ctl_table fs_table[] = { | |||
1225 | .extra1 = &minolduid, | 1248 | .extra1 = &minolduid, |
1226 | .extra2 = &maxolduid, | 1249 | .extra2 = &maxolduid, |
1227 | }, | 1250 | }, |
1251 | #ifdef CONFIG_FILE_LOCKING | ||
1228 | { | 1252 | { |
1229 | .ctl_name = FS_LEASES, | 1253 | .ctl_name = FS_LEASES, |
1230 | .procname = "leases-enable", | 1254 | .procname = "leases-enable", |
@@ -1233,6 +1257,7 @@ static struct ctl_table fs_table[] = { | |||
1233 | .mode = 0644, | 1257 | .mode = 0644, |
1234 | .proc_handler = &proc_dointvec, | 1258 | .proc_handler = &proc_dointvec, |
1235 | }, | 1259 | }, |
1260 | #endif | ||
1236 | #ifdef CONFIG_DNOTIFY | 1261 | #ifdef CONFIG_DNOTIFY |
1237 | { | 1262 | { |
1238 | .ctl_name = FS_DIR_NOTIFY, | 1263 | .ctl_name = FS_DIR_NOTIFY, |
@@ -1244,6 +1269,7 @@ static struct ctl_table fs_table[] = { | |||
1244 | }, | 1269 | }, |
1245 | #endif | 1270 | #endif |
1246 | #ifdef CONFIG_MMU | 1271 | #ifdef CONFIG_MMU |
1272 | #ifdef CONFIG_FILE_LOCKING | ||
1247 | { | 1273 | { |
1248 | .ctl_name = FS_LEASE_TIME, | 1274 | .ctl_name = FS_LEASE_TIME, |
1249 | .procname = "lease-break-time", | 1275 | .procname = "lease-break-time", |
@@ -1255,6 +1281,7 @@ static struct ctl_table fs_table[] = { | |||
1255 | .extra1 = &zero, | 1281 | .extra1 = &zero, |
1256 | .extra2 = &two, | 1282 | .extra2 = &two, |
1257 | }, | 1283 | }, |
1284 | #endif | ||
1258 | { | 1285 | { |
1259 | .procname = "aio-nr", | 1286 | .procname = "aio-nr", |
1260 | .data = &aio_nr, | 1287 | .data = &aio_nr, |
@@ -1352,6 +1379,9 @@ static void start_unregistering(struct ctl_table_header *p) | |||
1352 | spin_unlock(&sysctl_lock); | 1379 | spin_unlock(&sysctl_lock); |
1353 | wait_for_completion(&wait); | 1380 | wait_for_completion(&wait); |
1354 | spin_lock(&sysctl_lock); | 1381 | spin_lock(&sysctl_lock); |
1382 | } else { | ||
1383 | /* anything non-NULL; we'll never dereference it */ | ||
1384 | p->unregistering = ERR_PTR(-EINVAL); | ||
1355 | } | 1385 | } |
1356 | /* | 1386 | /* |
1357 | * do not remove from the list until nobody holds it; walking the | 1387 | * do not remove from the list until nobody holds it; walking the |
@@ -1360,6 +1390,32 @@ static void start_unregistering(struct ctl_table_header *p) | |||
1360 | list_del_init(&p->ctl_entry); | 1390 | list_del_init(&p->ctl_entry); |
1361 | } | 1391 | } |
1362 | 1392 | ||
1393 | void sysctl_head_get(struct ctl_table_header *head) | ||
1394 | { | ||
1395 | spin_lock(&sysctl_lock); | ||
1396 | head->count++; | ||
1397 | spin_unlock(&sysctl_lock); | ||
1398 | } | ||
1399 | |||
1400 | void sysctl_head_put(struct ctl_table_header *head) | ||
1401 | { | ||
1402 | spin_lock(&sysctl_lock); | ||
1403 | if (!--head->count) | ||
1404 | kfree(head); | ||
1405 | spin_unlock(&sysctl_lock); | ||
1406 | } | ||
1407 | |||
1408 | struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) | ||
1409 | { | ||
1410 | if (!head) | ||
1411 | BUG(); | ||
1412 | spin_lock(&sysctl_lock); | ||
1413 | if (!use_table(head)) | ||
1414 | head = ERR_PTR(-ENOENT); | ||
1415 | spin_unlock(&sysctl_lock); | ||
1416 | return head; | ||
1417 | } | ||
1418 | |||
1363 | void sysctl_head_finish(struct ctl_table_header *head) | 1419 | void sysctl_head_finish(struct ctl_table_header *head) |
1364 | { | 1420 | { |
1365 | if (!head) | 1421 | if (!head) |
@@ -1369,14 +1425,20 @@ void sysctl_head_finish(struct ctl_table_header *head) | |||
1369 | spin_unlock(&sysctl_lock); | 1425 | spin_unlock(&sysctl_lock); |
1370 | } | 1426 | } |
1371 | 1427 | ||
1428 | static struct ctl_table_set * | ||
1429 | lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces) | ||
1430 | { | ||
1431 | struct ctl_table_set *set = &root->default_set; | ||
1432 | if (root->lookup) | ||
1433 | set = root->lookup(root, namespaces); | ||
1434 | return set; | ||
1435 | } | ||
1436 | |||
1372 | static struct list_head * | 1437 | static struct list_head * |
1373 | lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) | 1438 | lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) |
1374 | { | 1439 | { |
1375 | struct list_head *header_list; | 1440 | struct ctl_table_set *set = lookup_header_set(root, namespaces); |
1376 | header_list = &root->header_list; | 1441 | return &set->list; |
1377 | if (root->lookup) | ||
1378 | header_list = root->lookup(root, namespaces); | ||
1379 | return header_list; | ||
1380 | } | 1442 | } |
1381 | 1443 | ||
1382 | struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | 1444 | struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, |
@@ -1446,9 +1508,9 @@ static int do_sysctl_strategy(struct ctl_table_root *root, | |||
1446 | int op = 0, rc; | 1508 | int op = 0, rc; |
1447 | 1509 | ||
1448 | if (oldval) | 1510 | if (oldval) |
1449 | op |= 004; | 1511 | op |= MAY_READ; |
1450 | if (newval) | 1512 | if (newval) |
1451 | op |= 002; | 1513 | op |= MAY_WRITE; |
1452 | if (sysctl_perm(root, table, op)) | 1514 | if (sysctl_perm(root, table, op)) |
1453 | return -EPERM; | 1515 | return -EPERM; |
1454 | 1516 | ||
@@ -1490,7 +1552,7 @@ repeat: | |||
1490 | if (n == table->ctl_name) { | 1552 | if (n == table->ctl_name) { |
1491 | int error; | 1553 | int error; |
1492 | if (table->child) { | 1554 | if (table->child) { |
1493 | if (sysctl_perm(root, table, 001)) | 1555 | if (sysctl_perm(root, table, MAY_EXEC)) |
1494 | return -EPERM; | 1556 | return -EPERM; |
1495 | name++; | 1557 | name++; |
1496 | nlen--; | 1558 | nlen--; |
@@ -1565,7 +1627,7 @@ static int test_perm(int mode, int op) | |||
1565 | mode >>= 6; | 1627 | mode >>= 6; |
1566 | else if (in_egroup_p(0)) | 1628 | else if (in_egroup_p(0)) |
1567 | mode >>= 3; | 1629 | mode >>= 3; |
1568 | if ((mode & op & 0007) == op) | 1630 | if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0) |
1569 | return 0; | 1631 | return 0; |
1570 | return -EACCES; | 1632 | return -EACCES; |
1571 | } | 1633 | } |
@@ -1575,7 +1637,7 @@ int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) | |||
1575 | int error; | 1637 | int error; |
1576 | int mode; | 1638 | int mode; |
1577 | 1639 | ||
1578 | error = security_sysctl(table, op); | 1640 | error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC)); |
1579 | if (error) | 1641 | if (error) |
1580 | return error; | 1642 | return error; |
1581 | 1643 | ||
@@ -1610,6 +1672,54 @@ static __init int sysctl_init(void) | |||
1610 | 1672 | ||
1611 | core_initcall(sysctl_init); | 1673 | core_initcall(sysctl_init); |
1612 | 1674 | ||
1675 | static struct ctl_table *is_branch_in(struct ctl_table *branch, | ||
1676 | struct ctl_table *table) | ||
1677 | { | ||
1678 | struct ctl_table *p; | ||
1679 | const char *s = branch->procname; | ||
1680 | |||
1681 | /* branch should have named subdirectory as its first element */ | ||
1682 | if (!s || !branch->child) | ||
1683 | return NULL; | ||
1684 | |||
1685 | /* ... and nothing else */ | ||
1686 | if (branch[1].procname || branch[1].ctl_name) | ||
1687 | return NULL; | ||
1688 | |||
1689 | /* table should contain subdirectory with the same name */ | ||
1690 | for (p = table; p->procname || p->ctl_name; p++) { | ||
1691 | if (!p->child) | ||
1692 | continue; | ||
1693 | if (p->procname && strcmp(p->procname, s) == 0) | ||
1694 | return p; | ||
1695 | } | ||
1696 | return NULL; | ||
1697 | } | ||
1698 | |||
1699 | /* see if attaching q to p would be an improvement */ | ||
1700 | static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) | ||
1701 | { | ||
1702 | struct ctl_table *to = p->ctl_table, *by = q->ctl_table; | ||
1703 | struct ctl_table *next; | ||
1704 | int is_better = 0; | ||
1705 | int not_in_parent = !p->attached_by; | ||
1706 | |||
1707 | while ((next = is_branch_in(by, to)) != NULL) { | ||
1708 | if (by == q->attached_by) | ||
1709 | is_better = 1; | ||
1710 | if (to == p->attached_by) | ||
1711 | not_in_parent = 1; | ||
1712 | by = by->child; | ||
1713 | to = next->child; | ||
1714 | } | ||
1715 | |||
1716 | if (is_better && not_in_parent) { | ||
1717 | q->attached_by = by; | ||
1718 | q->attached_to = to; | ||
1719 | q->parent = p; | ||
1720 | } | ||
1721 | } | ||
1722 | |||
1613 | /** | 1723 | /** |
1614 | * __register_sysctl_paths - register a sysctl hierarchy | 1724 | * __register_sysctl_paths - register a sysctl hierarchy |
1615 | * @root: List of sysctl headers to register on | 1725 | * @root: List of sysctl headers to register on |
@@ -1686,10 +1796,10 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1686 | struct nsproxy *namespaces, | 1796 | struct nsproxy *namespaces, |
1687 | const struct ctl_path *path, struct ctl_table *table) | 1797 | const struct ctl_path *path, struct ctl_table *table) |
1688 | { | 1798 | { |
1689 | struct list_head *header_list; | ||
1690 | struct ctl_table_header *header; | 1799 | struct ctl_table_header *header; |
1691 | struct ctl_table *new, **prevp; | 1800 | struct ctl_table *new, **prevp; |
1692 | unsigned int n, npath; | 1801 | unsigned int n, npath; |
1802 | struct ctl_table_set *set; | ||
1693 | 1803 | ||
1694 | /* Count the path components */ | 1804 | /* Count the path components */ |
1695 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) | 1805 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) |
@@ -1731,6 +1841,7 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1731 | header->unregistering = NULL; | 1841 | header->unregistering = NULL; |
1732 | header->root = root; | 1842 | header->root = root; |
1733 | sysctl_set_parent(NULL, header->ctl_table); | 1843 | sysctl_set_parent(NULL, header->ctl_table); |
1844 | header->count = 1; | ||
1734 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK | 1845 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK |
1735 | if (sysctl_check_table(namespaces, header->ctl_table)) { | 1846 | if (sysctl_check_table(namespaces, header->ctl_table)) { |
1736 | kfree(header); | 1847 | kfree(header); |
@@ -1738,8 +1849,20 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1738 | } | 1849 | } |
1739 | #endif | 1850 | #endif |
1740 | spin_lock(&sysctl_lock); | 1851 | spin_lock(&sysctl_lock); |
1741 | header_list = lookup_header_list(root, namespaces); | 1852 | header->set = lookup_header_set(root, namespaces); |
1742 | list_add_tail(&header->ctl_entry, header_list); | 1853 | header->attached_by = header->ctl_table; |
1854 | header->attached_to = root_table; | ||
1855 | header->parent = &root_table_header; | ||
1856 | for (set = header->set; set; set = set->parent) { | ||
1857 | struct ctl_table_header *p; | ||
1858 | list_for_each_entry(p, &set->list, ctl_entry) { | ||
1859 | if (p->unregistering) | ||
1860 | continue; | ||
1861 | try_attach(p, header); | ||
1862 | } | ||
1863 | } | ||
1864 | header->parent->count++; | ||
1865 | list_add_tail(&header->ctl_entry, &header->set->list); | ||
1743 | spin_unlock(&sysctl_lock); | 1866 | spin_unlock(&sysctl_lock); |
1744 | 1867 | ||
1745 | return header; | 1868 | return header; |
@@ -1794,8 +1917,37 @@ void unregister_sysctl_table(struct ctl_table_header * header) | |||
1794 | 1917 | ||
1795 | spin_lock(&sysctl_lock); | 1918 | spin_lock(&sysctl_lock); |
1796 | start_unregistering(header); | 1919 | start_unregistering(header); |
1920 | if (!--header->parent->count) { | ||
1921 | WARN_ON(1); | ||
1922 | kfree(header->parent); | ||
1923 | } | ||
1924 | if (!--header->count) | ||
1925 | kfree(header); | ||
1797 | spin_unlock(&sysctl_lock); | 1926 | spin_unlock(&sysctl_lock); |
1798 | kfree(header); | 1927 | } |
1928 | |||
1929 | int sysctl_is_seen(struct ctl_table_header *p) | ||
1930 | { | ||
1931 | struct ctl_table_set *set = p->set; | ||
1932 | int res; | ||
1933 | spin_lock(&sysctl_lock); | ||
1934 | if (p->unregistering) | ||
1935 | res = 0; | ||
1936 | else if (!set->is_seen) | ||
1937 | res = 1; | ||
1938 | else | ||
1939 | res = set->is_seen(set); | ||
1940 | spin_unlock(&sysctl_lock); | ||
1941 | return res; | ||
1942 | } | ||
1943 | |||
1944 | void setup_sysctl_set(struct ctl_table_set *p, | ||
1945 | struct ctl_table_set *parent, | ||
1946 | int (*is_seen)(struct ctl_table_set *)) | ||
1947 | { | ||
1948 | INIT_LIST_HEAD(&p->list); | ||
1949 | p->parent = parent ? parent : &sysctl_table_root.default_set; | ||
1950 | p->is_seen = is_seen; | ||
1799 | } | 1951 | } |
1800 | 1952 | ||
1801 | #else /* !CONFIG_SYSCTL */ | 1953 | #else /* !CONFIG_SYSCTL */ |
@@ -1814,6 +1966,16 @@ void unregister_sysctl_table(struct ctl_table_header * table) | |||
1814 | { | 1966 | { |
1815 | } | 1967 | } |
1816 | 1968 | ||
1969 | void setup_sysctl_set(struct ctl_table_set *p, | ||
1970 | struct ctl_table_set *parent, | ||
1971 | int (*is_seen)(struct ctl_table_set *)) | ||
1972 | { | ||
1973 | } | ||
1974 | |||
1975 | void sysctl_head_put(struct ctl_table_header *head) | ||
1976 | { | ||
1977 | } | ||
1978 | |||
1817 | #endif /* CONFIG_SYSCTL */ | 1979 | #endif /* CONFIG_SYSCTL */ |
1818 | 1980 | ||
1819 | /* | 1981 | /* |