diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/sysctl.c | 181 | ||||
-rw-r--r-- | kernel/sysctl_check.c | 25 |
2 files changed, 166 insertions, 40 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8e96558cb8f3..4bc8e48434a7 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -157,8 +157,16 @@ static int proc_dointvec_taint(struct ctl_table *table, int write, struct file * | |||
157 | #endif | 157 | #endif |
158 | 158 | ||
159 | static struct ctl_table root_table[]; | 159 | static struct ctl_table root_table[]; |
160 | static struct ctl_table_header root_table_header = | 160 | static struct ctl_table_root sysctl_table_root; |
161 | { root_table, LIST_HEAD_INIT(root_table_header.ctl_entry) }; | 161 | static struct ctl_table_header root_table_header = { |
162 | .ctl_table = root_table, | ||
163 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list), | ||
164 | .root = &sysctl_table_root, | ||
165 | }; | ||
166 | static struct ctl_table_root sysctl_table_root = { | ||
167 | .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), | ||
168 | .header_list = LIST_HEAD_INIT(root_table_header.ctl_entry), | ||
169 | }; | ||
162 | 170 | ||
163 | static struct ctl_table kern_table[]; | 171 | static struct ctl_table kern_table[]; |
164 | static struct ctl_table vm_table[]; | 172 | static struct ctl_table vm_table[]; |
@@ -192,14 +200,6 @@ static struct ctl_table root_table[] = { | |||
192 | .mode = 0555, | 200 | .mode = 0555, |
193 | .child = vm_table, | 201 | .child = vm_table, |
194 | }, | 202 | }, |
195 | #ifdef CONFIG_NET | ||
196 | { | ||
197 | .ctl_name = CTL_NET, | ||
198 | .procname = "net", | ||
199 | .mode = 0555, | ||
200 | .child = net_table, | ||
201 | }, | ||
202 | #endif | ||
203 | { | 203 | { |
204 | .ctl_name = CTL_FS, | 204 | .ctl_name = CTL_FS, |
205 | .procname = "fs", | 205 | .procname = "fs", |
@@ -1371,12 +1371,27 @@ void sysctl_head_finish(struct ctl_table_header *head) | |||
1371 | spin_unlock(&sysctl_lock); | 1371 | spin_unlock(&sysctl_lock); |
1372 | } | 1372 | } |
1373 | 1373 | ||
1374 | struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | 1374 | static struct list_head * |
1375 | lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) | ||
1375 | { | 1376 | { |
1377 | struct list_head *header_list; | ||
1378 | header_list = &root->header_list; | ||
1379 | if (root->lookup) | ||
1380 | header_list = root->lookup(root, namespaces); | ||
1381 | return header_list; | ||
1382 | } | ||
1383 | |||
1384 | struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | ||
1385 | struct ctl_table_header *prev) | ||
1386 | { | ||
1387 | struct ctl_table_root *root; | ||
1388 | struct list_head *header_list; | ||
1376 | struct ctl_table_header *head; | 1389 | struct ctl_table_header *head; |
1377 | struct list_head *tmp; | 1390 | struct list_head *tmp; |
1391 | |||
1378 | spin_lock(&sysctl_lock); | 1392 | spin_lock(&sysctl_lock); |
1379 | if (prev) { | 1393 | if (prev) { |
1394 | head = prev; | ||
1380 | tmp = &prev->ctl_entry; | 1395 | tmp = &prev->ctl_entry; |
1381 | unuse_table(prev); | 1396 | unuse_table(prev); |
1382 | goto next; | 1397 | goto next; |
@@ -1390,14 +1405,38 @@ struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | |||
1390 | spin_unlock(&sysctl_lock); | 1405 | spin_unlock(&sysctl_lock); |
1391 | return head; | 1406 | return head; |
1392 | next: | 1407 | next: |
1408 | root = head->root; | ||
1393 | tmp = tmp->next; | 1409 | tmp = tmp->next; |
1394 | if (tmp == &root_table_header.ctl_entry) | 1410 | header_list = lookup_header_list(root, namespaces); |
1395 | break; | 1411 | if (tmp != header_list) |
1412 | continue; | ||
1413 | |||
1414 | do { | ||
1415 | root = list_entry(root->root_list.next, | ||
1416 | struct ctl_table_root, root_list); | ||
1417 | if (root == &sysctl_table_root) | ||
1418 | goto out; | ||
1419 | header_list = lookup_header_list(root, namespaces); | ||
1420 | } while (list_empty(header_list)); | ||
1421 | tmp = header_list->next; | ||
1396 | } | 1422 | } |
1423 | out: | ||
1397 | spin_unlock(&sysctl_lock); | 1424 | spin_unlock(&sysctl_lock); |
1398 | return NULL; | 1425 | return NULL; |
1399 | } | 1426 | } |
1400 | 1427 | ||
1428 | struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | ||
1429 | { | ||
1430 | return __sysctl_head_next(current->nsproxy, prev); | ||
1431 | } | ||
1432 | |||
1433 | void register_sysctl_root(struct ctl_table_root *root) | ||
1434 | { | ||
1435 | spin_lock(&sysctl_lock); | ||
1436 | list_add_tail(&root->root_list, &sysctl_table_root.root_list); | ||
1437 | spin_unlock(&sysctl_lock); | ||
1438 | } | ||
1439 | |||
1401 | #ifdef CONFIG_SYSCTL_SYSCALL | 1440 | #ifdef CONFIG_SYSCTL_SYSCALL |
1402 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, | 1441 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, |
1403 | void __user *newval, size_t newlen) | 1442 | void __user *newval, size_t newlen) |
@@ -1554,18 +1593,21 @@ static __init int sysctl_init(void) | |||
1554 | { | 1593 | { |
1555 | int err; | 1594 | int err; |
1556 | sysctl_set_parent(NULL, root_table); | 1595 | sysctl_set_parent(NULL, root_table); |
1557 | err = sysctl_check_table(root_table); | 1596 | err = sysctl_check_table(current->nsproxy, root_table); |
1558 | return 0; | 1597 | return 0; |
1559 | } | 1598 | } |
1560 | 1599 | ||
1561 | core_initcall(sysctl_init); | 1600 | core_initcall(sysctl_init); |
1562 | 1601 | ||
1563 | /** | 1602 | /** |
1564 | * register_sysctl_table - register a sysctl hierarchy | 1603 | * __register_sysctl_paths - register a sysctl hierarchy |
1604 | * @root: List of sysctl headers to register on | ||
1605 | * @namespaces: Data to compute which lists of sysctl entries are visible | ||
1606 | * @path: The path to the directory the sysctl table is in. | ||
1565 | * @table: the top-level table structure | 1607 | * @table: the top-level table structure |
1566 | * | 1608 | * |
1567 | * Register a sysctl table hierarchy. @table should be a filled in ctl_table | 1609 | * Register a sysctl table hierarchy. @table should be a filled in ctl_table |
1568 | * array. An entry with a ctl_name of 0 terminates the table. | 1610 | * array. A completely 0 filled entry terminates the table. |
1569 | * | 1611 | * |
1570 | * The members of the &struct ctl_table structure are used as follows: | 1612 | * The members of the &struct ctl_table structure are used as follows: |
1571 | * | 1613 | * |
@@ -1628,25 +1670,99 @@ core_initcall(sysctl_init); | |||
1628 | * This routine returns %NULL on a failure to register, and a pointer | 1670 | * This routine returns %NULL on a failure to register, and a pointer |
1629 | * to the table header on success. | 1671 | * to the table header on success. |
1630 | */ | 1672 | */ |
1631 | struct ctl_table_header *register_sysctl_table(struct ctl_table * table) | 1673 | struct ctl_table_header *__register_sysctl_paths( |
1674 | struct ctl_table_root *root, | ||
1675 | struct nsproxy *namespaces, | ||
1676 | const struct ctl_path *path, struct ctl_table *table) | ||
1632 | { | 1677 | { |
1633 | struct ctl_table_header *tmp; | 1678 | struct list_head *header_list; |
1634 | tmp = kmalloc(sizeof(struct ctl_table_header), GFP_KERNEL); | 1679 | struct ctl_table_header *header; |
1635 | if (!tmp) | 1680 | struct ctl_table *new, **prevp; |
1681 | unsigned int n, npath; | ||
1682 | |||
1683 | /* Count the path components */ | ||
1684 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) | ||
1685 | ; | ||
1686 | |||
1687 | /* | ||
1688 | * For each path component, allocate a 2-element ctl_table array. | ||
1689 | * The first array element will be filled with the sysctl entry | ||
1690 | * for this, the second will be the sentinel (ctl_name == 0). | ||
1691 | * | ||
1692 | * We allocate everything in one go so that we don't have to | ||
1693 | * worry about freeing additional memory in unregister_sysctl_table. | ||
1694 | */ | ||
1695 | header = kzalloc(sizeof(struct ctl_table_header) + | ||
1696 | (2 * npath * sizeof(struct ctl_table)), GFP_KERNEL); | ||
1697 | if (!header) | ||
1636 | return NULL; | 1698 | return NULL; |
1637 | tmp->ctl_table = table; | 1699 | |
1638 | INIT_LIST_HEAD(&tmp->ctl_entry); | 1700 | new = (struct ctl_table *) (header + 1); |
1639 | tmp->used = 0; | 1701 | |
1640 | tmp->unregistering = NULL; | 1702 | /* Now connect the dots */ |
1641 | sysctl_set_parent(NULL, table); | 1703 | prevp = &header->ctl_table; |
1642 | if (sysctl_check_table(tmp->ctl_table)) { | 1704 | for (n = 0; n < npath; ++n, ++path) { |
1643 | kfree(tmp); | 1705 | /* Copy the procname */ |
1706 | new->procname = path->procname; | ||
1707 | new->ctl_name = path->ctl_name; | ||
1708 | new->mode = 0555; | ||
1709 | |||
1710 | *prevp = new; | ||
1711 | prevp = &new->child; | ||
1712 | |||
1713 | new += 2; | ||
1714 | } | ||
1715 | *prevp = table; | ||
1716 | header->ctl_table_arg = table; | ||
1717 | |||
1718 | INIT_LIST_HEAD(&header->ctl_entry); | ||
1719 | header->used = 0; | ||
1720 | header->unregistering = NULL; | ||
1721 | header->root = root; | ||
1722 | sysctl_set_parent(NULL, header->ctl_table); | ||
1723 | if (sysctl_check_table(namespaces, header->ctl_table)) { | ||
1724 | kfree(header); | ||
1644 | return NULL; | 1725 | return NULL; |
1645 | } | 1726 | } |
1646 | spin_lock(&sysctl_lock); | 1727 | spin_lock(&sysctl_lock); |
1647 | list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry); | 1728 | header_list = lookup_header_list(root, namespaces); |
1729 | list_add_tail(&header->ctl_entry, header_list); | ||
1648 | spin_unlock(&sysctl_lock); | 1730 | spin_unlock(&sysctl_lock); |
1649 | return tmp; | 1731 | |
1732 | return header; | ||
1733 | } | ||
1734 | |||
1735 | /** | ||
1736 | * register_sysctl_table_path - register a sysctl table hierarchy | ||
1737 | * @path: The path to the directory the sysctl table is in. | ||
1738 | * @table: the top-level table structure | ||
1739 | * | ||
1740 | * Register a sysctl table hierarchy. @table should be a filled in ctl_table | ||
1741 | * array. A completely 0 filled entry terminates the table. | ||
1742 | * | ||
1743 | * See __register_sysctl_paths for more details. | ||
1744 | */ | ||
1745 | struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | ||
1746 | struct ctl_table *table) | ||
1747 | { | ||
1748 | return __register_sysctl_paths(&sysctl_table_root, current->nsproxy, | ||
1749 | path, table); | ||
1750 | } | ||
1751 | |||
1752 | /** | ||
1753 | * register_sysctl_table - register a sysctl table hierarchy | ||
1754 | * @table: the top-level table structure | ||
1755 | * | ||
1756 | * Register a sysctl table hierarchy. @table should be a filled in ctl_table | ||
1757 | * array. A completely 0 filled entry terminates the table. | ||
1758 | * | ||
1759 | * See register_sysctl_paths for more details. | ||
1760 | */ | ||
1761 | struct ctl_table_header *register_sysctl_table(struct ctl_table *table) | ||
1762 | { | ||
1763 | static const struct ctl_path null_path[] = { {} }; | ||
1764 | |||
1765 | return register_sysctl_paths(null_path, table); | ||
1650 | } | 1766 | } |
1651 | 1767 | ||
1652 | /** | 1768 | /** |
@@ -1675,6 +1791,12 @@ struct ctl_table_header *register_sysctl_table(struct ctl_table * table) | |||
1675 | return NULL; | 1791 | return NULL; |
1676 | } | 1792 | } |
1677 | 1793 | ||
1794 | struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | ||
1795 | struct ctl_table *table) | ||
1796 | { | ||
1797 | return NULL; | ||
1798 | } | ||
1799 | |||
1678 | void unregister_sysctl_table(struct ctl_table_header * table) | 1800 | void unregister_sysctl_table(struct ctl_table_header * table) |
1679 | { | 1801 | { |
1680 | } | 1802 | } |
@@ -2733,6 +2855,7 @@ EXPORT_SYMBOL(proc_dostring); | |||
2733 | EXPORT_SYMBOL(proc_doulongvec_minmax); | 2855 | EXPORT_SYMBOL(proc_doulongvec_minmax); |
2734 | EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); | 2856 | EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); |
2735 | EXPORT_SYMBOL(register_sysctl_table); | 2857 | EXPORT_SYMBOL(register_sysctl_table); |
2858 | EXPORT_SYMBOL(register_sysctl_paths); | ||
2736 | EXPORT_SYMBOL(sysctl_intvec); | 2859 | EXPORT_SYMBOL(sysctl_intvec); |
2737 | EXPORT_SYMBOL(sysctl_jiffies); | 2860 | EXPORT_SYMBOL(sysctl_jiffies); |
2738 | EXPORT_SYMBOL(sysctl_ms_jiffies); | 2861 | EXPORT_SYMBOL(sysctl_ms_jiffies); |
diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c index d8a5558a47b4..c3206fa50048 100644 --- a/kernel/sysctl_check.c +++ b/kernel/sysctl_check.c | |||
@@ -1342,7 +1342,8 @@ static void sysctl_repair_table(struct ctl_table *table) | |||
1342 | } | 1342 | } |
1343 | } | 1343 | } |
1344 | 1344 | ||
1345 | static struct ctl_table *sysctl_check_lookup(struct ctl_table *table) | 1345 | static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces, |
1346 | struct ctl_table *table) | ||
1346 | { | 1347 | { |
1347 | struct ctl_table_header *head; | 1348 | struct ctl_table_header *head; |
1348 | struct ctl_table *ref, *test; | 1349 | struct ctl_table *ref, *test; |
@@ -1350,8 +1351,8 @@ static struct ctl_table *sysctl_check_lookup(struct ctl_table *table) | |||
1350 | 1351 | ||
1351 | depth = sysctl_depth(table); | 1352 | depth = sysctl_depth(table); |
1352 | 1353 | ||
1353 | for (head = sysctl_head_next(NULL); head; | 1354 | for (head = __sysctl_head_next(namespaces, NULL); head; |
1354 | head = sysctl_head_next(head)) { | 1355 | head = __sysctl_head_next(namespaces, head)) { |
1355 | cur_depth = depth; | 1356 | cur_depth = depth; |
1356 | ref = head->ctl_table; | 1357 | ref = head->ctl_table; |
1357 | repeat: | 1358 | repeat: |
@@ -1396,13 +1397,14 @@ static void set_fail(const char **fail, struct ctl_table *table, const char *str | |||
1396 | *fail = str; | 1397 | *fail = str; |
1397 | } | 1398 | } |
1398 | 1399 | ||
1399 | static int sysctl_check_dir(struct ctl_table *table) | 1400 | static int sysctl_check_dir(struct nsproxy *namespaces, |
1401 | struct ctl_table *table) | ||
1400 | { | 1402 | { |
1401 | struct ctl_table *ref; | 1403 | struct ctl_table *ref; |
1402 | int error; | 1404 | int error; |
1403 | 1405 | ||
1404 | error = 0; | 1406 | error = 0; |
1405 | ref = sysctl_check_lookup(table); | 1407 | ref = sysctl_check_lookup(namespaces, table); |
1406 | if (ref) { | 1408 | if (ref) { |
1407 | int match = 0; | 1409 | int match = 0; |
1408 | if ((!table->procname && !ref->procname) || | 1410 | if ((!table->procname && !ref->procname) || |
@@ -1427,11 +1429,12 @@ static int sysctl_check_dir(struct ctl_table *table) | |||
1427 | return error; | 1429 | return error; |
1428 | } | 1430 | } |
1429 | 1431 | ||
1430 | static void sysctl_check_leaf(struct ctl_table *table, const char **fail) | 1432 | static void sysctl_check_leaf(struct nsproxy *namespaces, |
1433 | struct ctl_table *table, const char **fail) | ||
1431 | { | 1434 | { |
1432 | struct ctl_table *ref; | 1435 | struct ctl_table *ref; |
1433 | 1436 | ||
1434 | ref = sysctl_check_lookup(table); | 1437 | ref = sysctl_check_lookup(namespaces, table); |
1435 | if (ref && (ref != table)) | 1438 | if (ref && (ref != table)) |
1436 | set_fail(fail, table, "Sysctl already exists"); | 1439 | set_fail(fail, table, "Sysctl already exists"); |
1437 | } | 1440 | } |
@@ -1455,7 +1458,7 @@ static void sysctl_check_bin_path(struct ctl_table *table, const char **fail) | |||
1455 | } | 1458 | } |
1456 | } | 1459 | } |
1457 | 1460 | ||
1458 | int sysctl_check_table(struct ctl_table *table) | 1461 | int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table) |
1459 | { | 1462 | { |
1460 | int error = 0; | 1463 | int error = 0; |
1461 | for (; table->ctl_name || table->procname; table++) { | 1464 | for (; table->ctl_name || table->procname; table++) { |
@@ -1485,7 +1488,7 @@ int sysctl_check_table(struct ctl_table *table) | |||
1485 | set_fail(&fail, table, "Directory with extra1"); | 1488 | set_fail(&fail, table, "Directory with extra1"); |
1486 | if (table->extra2) | 1489 | if (table->extra2) |
1487 | set_fail(&fail, table, "Directory with extra2"); | 1490 | set_fail(&fail, table, "Directory with extra2"); |
1488 | if (sysctl_check_dir(table)) | 1491 | if (sysctl_check_dir(namespaces, table)) |
1489 | set_fail(&fail, table, "Inconsistent directory names"); | 1492 | set_fail(&fail, table, "Inconsistent directory names"); |
1490 | } else { | 1493 | } else { |
1491 | if ((table->strategy == sysctl_data) || | 1494 | if ((table->strategy == sysctl_data) || |
@@ -1534,7 +1537,7 @@ int sysctl_check_table(struct ctl_table *table) | |||
1534 | if (!table->procname && table->proc_handler) | 1537 | if (!table->procname && table->proc_handler) |
1535 | set_fail(&fail, table, "proc_handler without procname"); | 1538 | set_fail(&fail, table, "proc_handler without procname"); |
1536 | #endif | 1539 | #endif |
1537 | sysctl_check_leaf(table, &fail); | 1540 | sysctl_check_leaf(namespaces, table, &fail); |
1538 | } | 1541 | } |
1539 | sysctl_check_bin_path(table, &fail); | 1542 | sysctl_check_bin_path(table, &fail); |
1540 | if (fail) { | 1543 | if (fail) { |
@@ -1542,7 +1545,7 @@ int sysctl_check_table(struct ctl_table *table) | |||
1542 | error = -EINVAL; | 1545 | error = -EINVAL; |
1543 | } | 1546 | } |
1544 | if (table->child) | 1547 | if (table->child) |
1545 | error |= sysctl_check_table(table->child); | 1548 | error |= sysctl_check_table(namespaces, table->child); |
1546 | } | 1549 | } |
1547 | return error; | 1550 | return error; |
1548 | } | 1551 | } |