diff options
Diffstat (limited to 'kernel/sysctl.c')
-rw-r--r-- | kernel/sysctl.c | 177 |
1 files changed, 156 insertions, 21 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 35a50db9b6ce..1bf369bd4423 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -118,10 +118,8 @@ extern char modprobe_path[]; | |||
118 | extern int sg_big_buff; | 118 | extern int sg_big_buff; |
119 | #endif | 119 | #endif |
120 | 120 | ||
121 | #ifdef __sparc__ | 121 | #ifdef CONFIG_SPARC |
122 | extern char reboot_command []; | 122 | #include <asm/system.h> |
123 | extern int stop_a_enabled; | ||
124 | extern int scons_pwroff; | ||
125 | #endif | 123 | #endif |
126 | 124 | ||
127 | #ifdef __hppa__ | 125 | #ifdef __hppa__ |
@@ -159,13 +157,15 @@ static int proc_dointvec_taint(struct ctl_table *table, int write, struct file * | |||
159 | static struct ctl_table root_table[]; | 157 | static struct ctl_table root_table[]; |
160 | static struct ctl_table_root sysctl_table_root; | 158 | static struct ctl_table_root sysctl_table_root; |
161 | static struct ctl_table_header root_table_header = { | 159 | static struct ctl_table_header root_table_header = { |
160 | .count = 1, | ||
162 | .ctl_table = root_table, | 161 | .ctl_table = root_table, |
163 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list), | 162 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list), |
164 | .root = &sysctl_table_root, | 163 | .root = &sysctl_table_root, |
164 | .set = &sysctl_table_root.default_set, | ||
165 | }; | 165 | }; |
166 | static struct ctl_table_root sysctl_table_root = { | 166 | static struct ctl_table_root sysctl_table_root = { |
167 | .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), | 167 | .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), |
168 | .header_list = LIST_HEAD_INIT(root_table_header.ctl_entry), | 168 | .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), |
169 | }; | 169 | }; |
170 | 170 | ||
171 | static struct ctl_table kern_table[]; | 171 | static struct ctl_table kern_table[]; |
@@ -413,7 +413,7 @@ static struct ctl_table kern_table[] = { | |||
413 | .mode = 0644, | 413 | .mode = 0644, |
414 | .proc_handler = &proc_dointvec, | 414 | .proc_handler = &proc_dointvec, |
415 | }, | 415 | }, |
416 | #ifdef __sparc__ | 416 | #ifdef CONFIG_SPARC |
417 | { | 417 | { |
418 | .ctl_name = KERN_SPARC_REBOOT, | 418 | .ctl_name = KERN_SPARC_REBOOT, |
419 | .procname = "reboot-cmd", | 419 | .procname = "reboot-cmd", |
@@ -1386,6 +1386,9 @@ static void start_unregistering(struct ctl_table_header *p) | |||
1386 | spin_unlock(&sysctl_lock); | 1386 | spin_unlock(&sysctl_lock); |
1387 | wait_for_completion(&wait); | 1387 | wait_for_completion(&wait); |
1388 | spin_lock(&sysctl_lock); | 1388 | spin_lock(&sysctl_lock); |
1389 | } else { | ||
1390 | /* anything non-NULL; we'll never dereference it */ | ||
1391 | p->unregistering = ERR_PTR(-EINVAL); | ||
1389 | } | 1392 | } |
1390 | /* | 1393 | /* |
1391 | * do not remove from the list until nobody holds it; walking the | 1394 | * do not remove from the list until nobody holds it; walking the |
@@ -1394,6 +1397,32 @@ static void start_unregistering(struct ctl_table_header *p) | |||
1394 | list_del_init(&p->ctl_entry); | 1397 | list_del_init(&p->ctl_entry); |
1395 | } | 1398 | } |
1396 | 1399 | ||
1400 | void sysctl_head_get(struct ctl_table_header *head) | ||
1401 | { | ||
1402 | spin_lock(&sysctl_lock); | ||
1403 | head->count++; | ||
1404 | spin_unlock(&sysctl_lock); | ||
1405 | } | ||
1406 | |||
1407 | void sysctl_head_put(struct ctl_table_header *head) | ||
1408 | { | ||
1409 | spin_lock(&sysctl_lock); | ||
1410 | if (!--head->count) | ||
1411 | kfree(head); | ||
1412 | spin_unlock(&sysctl_lock); | ||
1413 | } | ||
1414 | |||
1415 | struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) | ||
1416 | { | ||
1417 | if (!head) | ||
1418 | BUG(); | ||
1419 | spin_lock(&sysctl_lock); | ||
1420 | if (!use_table(head)) | ||
1421 | head = ERR_PTR(-ENOENT); | ||
1422 | spin_unlock(&sysctl_lock); | ||
1423 | return head; | ||
1424 | } | ||
1425 | |||
1397 | void sysctl_head_finish(struct ctl_table_header *head) | 1426 | void sysctl_head_finish(struct ctl_table_header *head) |
1398 | { | 1427 | { |
1399 | if (!head) | 1428 | if (!head) |
@@ -1403,14 +1432,20 @@ void sysctl_head_finish(struct ctl_table_header *head) | |||
1403 | spin_unlock(&sysctl_lock); | 1432 | spin_unlock(&sysctl_lock); |
1404 | } | 1433 | } |
1405 | 1434 | ||
1435 | static struct ctl_table_set * | ||
1436 | lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces) | ||
1437 | { | ||
1438 | struct ctl_table_set *set = &root->default_set; | ||
1439 | if (root->lookup) | ||
1440 | set = root->lookup(root, namespaces); | ||
1441 | return set; | ||
1442 | } | ||
1443 | |||
1406 | static struct list_head * | 1444 | static struct list_head * |
1407 | lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) | 1445 | lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) |
1408 | { | 1446 | { |
1409 | struct list_head *header_list; | 1447 | struct ctl_table_set *set = lookup_header_set(root, namespaces); |
1410 | header_list = &root->header_list; | 1448 | return &set->list; |
1411 | if (root->lookup) | ||
1412 | header_list = root->lookup(root, namespaces); | ||
1413 | return header_list; | ||
1414 | } | 1449 | } |
1415 | 1450 | ||
1416 | struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | 1451 | struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, |
@@ -1480,9 +1515,9 @@ static int do_sysctl_strategy(struct ctl_table_root *root, | |||
1480 | int op = 0, rc; | 1515 | int op = 0, rc; |
1481 | 1516 | ||
1482 | if (oldval) | 1517 | if (oldval) |
1483 | op |= 004; | 1518 | op |= MAY_READ; |
1484 | if (newval) | 1519 | if (newval) |
1485 | op |= 002; | 1520 | op |= MAY_WRITE; |
1486 | if (sysctl_perm(root, table, op)) | 1521 | if (sysctl_perm(root, table, op)) |
1487 | return -EPERM; | 1522 | return -EPERM; |
1488 | 1523 | ||
@@ -1524,7 +1559,7 @@ repeat: | |||
1524 | if (n == table->ctl_name) { | 1559 | if (n == table->ctl_name) { |
1525 | int error; | 1560 | int error; |
1526 | if (table->child) { | 1561 | if (table->child) { |
1527 | if (sysctl_perm(root, table, 001)) | 1562 | if (sysctl_perm(root, table, MAY_EXEC)) |
1528 | return -EPERM; | 1563 | return -EPERM; |
1529 | name++; | 1564 | name++; |
1530 | nlen--; | 1565 | nlen--; |
@@ -1599,7 +1634,7 @@ static int test_perm(int mode, int op) | |||
1599 | mode >>= 6; | 1634 | mode >>= 6; |
1600 | else if (in_egroup_p(0)) | 1635 | else if (in_egroup_p(0)) |
1601 | mode >>= 3; | 1636 | mode >>= 3; |
1602 | if ((mode & op & 0007) == op) | 1637 | if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0) |
1603 | return 0; | 1638 | return 0; |
1604 | return -EACCES; | 1639 | return -EACCES; |
1605 | } | 1640 | } |
@@ -1609,7 +1644,7 @@ int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) | |||
1609 | int error; | 1644 | int error; |
1610 | int mode; | 1645 | int mode; |
1611 | 1646 | ||
1612 | error = security_sysctl(table, op); | 1647 | error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC)); |
1613 | if (error) | 1648 | if (error) |
1614 | return error; | 1649 | return error; |
1615 | 1650 | ||
@@ -1644,6 +1679,54 @@ static __init int sysctl_init(void) | |||
1644 | 1679 | ||
1645 | core_initcall(sysctl_init); | 1680 | core_initcall(sysctl_init); |
1646 | 1681 | ||
1682 | static struct ctl_table *is_branch_in(struct ctl_table *branch, | ||
1683 | struct ctl_table *table) | ||
1684 | { | ||
1685 | struct ctl_table *p; | ||
1686 | const char *s = branch->procname; | ||
1687 | |||
1688 | /* branch should have named subdirectory as its first element */ | ||
1689 | if (!s || !branch->child) | ||
1690 | return NULL; | ||
1691 | |||
1692 | /* ... and nothing else */ | ||
1693 | if (branch[1].procname || branch[1].ctl_name) | ||
1694 | return NULL; | ||
1695 | |||
1696 | /* table should contain subdirectory with the same name */ | ||
1697 | for (p = table; p->procname || p->ctl_name; p++) { | ||
1698 | if (!p->child) | ||
1699 | continue; | ||
1700 | if (p->procname && strcmp(p->procname, s) == 0) | ||
1701 | return p; | ||
1702 | } | ||
1703 | return NULL; | ||
1704 | } | ||
1705 | |||
1706 | /* see if attaching q to p would be an improvement */ | ||
1707 | static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) | ||
1708 | { | ||
1709 | struct ctl_table *to = p->ctl_table, *by = q->ctl_table; | ||
1710 | struct ctl_table *next; | ||
1711 | int is_better = 0; | ||
1712 | int not_in_parent = !p->attached_by; | ||
1713 | |||
1714 | while ((next = is_branch_in(by, to)) != NULL) { | ||
1715 | if (by == q->attached_by) | ||
1716 | is_better = 1; | ||
1717 | if (to == p->attached_by) | ||
1718 | not_in_parent = 1; | ||
1719 | by = by->child; | ||
1720 | to = next->child; | ||
1721 | } | ||
1722 | |||
1723 | if (is_better && not_in_parent) { | ||
1724 | q->attached_by = by; | ||
1725 | q->attached_to = to; | ||
1726 | q->parent = p; | ||
1727 | } | ||
1728 | } | ||
1729 | |||
1647 | /** | 1730 | /** |
1648 | * __register_sysctl_paths - register a sysctl hierarchy | 1731 | * __register_sysctl_paths - register a sysctl hierarchy |
1649 | * @root: List of sysctl headers to register on | 1732 | * @root: List of sysctl headers to register on |
@@ -1720,10 +1803,10 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1720 | struct nsproxy *namespaces, | 1803 | struct nsproxy *namespaces, |
1721 | const struct ctl_path *path, struct ctl_table *table) | 1804 | const struct ctl_path *path, struct ctl_table *table) |
1722 | { | 1805 | { |
1723 | struct list_head *header_list; | ||
1724 | struct ctl_table_header *header; | 1806 | struct ctl_table_header *header; |
1725 | struct ctl_table *new, **prevp; | 1807 | struct ctl_table *new, **prevp; |
1726 | unsigned int n, npath; | 1808 | unsigned int n, npath; |
1809 | struct ctl_table_set *set; | ||
1727 | 1810 | ||
1728 | /* Count the path components */ | 1811 | /* Count the path components */ |
1729 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) | 1812 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) |
@@ -1765,6 +1848,7 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1765 | header->unregistering = NULL; | 1848 | header->unregistering = NULL; |
1766 | header->root = root; | 1849 | header->root = root; |
1767 | sysctl_set_parent(NULL, header->ctl_table); | 1850 | sysctl_set_parent(NULL, header->ctl_table); |
1851 | header->count = 1; | ||
1768 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK | 1852 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK |
1769 | if (sysctl_check_table(namespaces, header->ctl_table)) { | 1853 | if (sysctl_check_table(namespaces, header->ctl_table)) { |
1770 | kfree(header); | 1854 | kfree(header); |
@@ -1772,8 +1856,20 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1772 | } | 1856 | } |
1773 | #endif | 1857 | #endif |
1774 | spin_lock(&sysctl_lock); | 1858 | spin_lock(&sysctl_lock); |
1775 | header_list = lookup_header_list(root, namespaces); | 1859 | header->set = lookup_header_set(root, namespaces); |
1776 | list_add_tail(&header->ctl_entry, header_list); | 1860 | header->attached_by = header->ctl_table; |
1861 | header->attached_to = root_table; | ||
1862 | header->parent = &root_table_header; | ||
1863 | for (set = header->set; set; set = set->parent) { | ||
1864 | struct ctl_table_header *p; | ||
1865 | list_for_each_entry(p, &set->list, ctl_entry) { | ||
1866 | if (p->unregistering) | ||
1867 | continue; | ||
1868 | try_attach(p, header); | ||
1869 | } | ||
1870 | } | ||
1871 | header->parent->count++; | ||
1872 | list_add_tail(&header->ctl_entry, &header->set->list); | ||
1777 | spin_unlock(&sysctl_lock); | 1873 | spin_unlock(&sysctl_lock); |
1778 | 1874 | ||
1779 | return header; | 1875 | return header; |
@@ -1828,8 +1924,37 @@ void unregister_sysctl_table(struct ctl_table_header * header) | |||
1828 | 1924 | ||
1829 | spin_lock(&sysctl_lock); | 1925 | spin_lock(&sysctl_lock); |
1830 | start_unregistering(header); | 1926 | start_unregistering(header); |
1927 | if (!--header->parent->count) { | ||
1928 | WARN_ON(1); | ||
1929 | kfree(header->parent); | ||
1930 | } | ||
1931 | if (!--header->count) | ||
1932 | kfree(header); | ||
1831 | spin_unlock(&sysctl_lock); | 1933 | spin_unlock(&sysctl_lock); |
1832 | kfree(header); | 1934 | } |
1935 | |||
1936 | int sysctl_is_seen(struct ctl_table_header *p) | ||
1937 | { | ||
1938 | struct ctl_table_set *set = p->set; | ||
1939 | int res; | ||
1940 | spin_lock(&sysctl_lock); | ||
1941 | if (p->unregistering) | ||
1942 | res = 0; | ||
1943 | else if (!set->is_seen) | ||
1944 | res = 1; | ||
1945 | else | ||
1946 | res = set->is_seen(set); | ||
1947 | spin_unlock(&sysctl_lock); | ||
1948 | return res; | ||
1949 | } | ||
1950 | |||
1951 | void setup_sysctl_set(struct ctl_table_set *p, | ||
1952 | struct ctl_table_set *parent, | ||
1953 | int (*is_seen)(struct ctl_table_set *)) | ||
1954 | { | ||
1955 | INIT_LIST_HEAD(&p->list); | ||
1956 | p->parent = parent ? parent : &sysctl_table_root.default_set; | ||
1957 | p->is_seen = is_seen; | ||
1833 | } | 1958 | } |
1834 | 1959 | ||
1835 | #else /* !CONFIG_SYSCTL */ | 1960 | #else /* !CONFIG_SYSCTL */ |
@@ -1848,6 +1973,16 @@ void unregister_sysctl_table(struct ctl_table_header * table) | |||
1848 | { | 1973 | { |
1849 | } | 1974 | } |
1850 | 1975 | ||
1976 | void setup_sysctl_set(struct ctl_table_set *p, | ||
1977 | struct ctl_table_set *parent, | ||
1978 | int (*is_seen)(struct ctl_table_set *)) | ||
1979 | { | ||
1980 | } | ||
1981 | |||
1982 | void sysctl_head_put(struct ctl_table_header *head) | ||
1983 | { | ||
1984 | } | ||
1985 | |||
1851 | #endif /* CONFIG_SYSCTL */ | 1986 | #endif /* CONFIG_SYSCTL */ |
1852 | 1987 | ||
1853 | /* | 1988 | /* |