Commit e51b6ba077791f2f8c876022b37419be7a2ceec3
Committed by
David S. Miller
1 parent
23eb06de7d
Exists in
master
and in
39 other branches
sysctl: Infrastructure for per namespace sysctls
This patch implements the basic infrastructure for per namespace sysctls. A list of lists of sysctl headers is added, allowing each namespace to have it's own list of sysctl headers. Each list of sysctl headers has a lookup function to find the first sysctl header in the list, allowing the lists to have a per namespace instance. register_sysct_root is added to tell sysctl.c about additional lists of sysctl_headers. As all of the users are expected to be in kernel no unregister function is provided. sysctl_head_next is updated to walk through the list of lists. __register_sysctl_paths is added to add a new sysctl table on a non-default sysctl list. The only intrusive part of this patch is propagating the information to decided which list of sysctls to use for sysctl_check_table. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Cc: Serge Hallyn <serue@us.ibm.com> Cc: Daniel Lezcano <dlezcano@fr.ibm.com> Cc: Cedric Le Goater <clg@fr.ibm.com> Cc: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 3 changed files with 112 additions and 23 deletions Side-by-side Diff
include/linux/sysctl.h
... | ... | @@ -945,7 +945,10 @@ |
945 | 945 | |
946 | 946 | /* For the /proc/sys support */ |
947 | 947 | struct ctl_table; |
948 | +struct nsproxy; | |
948 | 949 | extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); |
950 | +extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | |
951 | + struct ctl_table_header *prev); | |
949 | 952 | extern void sysctl_head_finish(struct ctl_table_header *prev); |
950 | 953 | extern int sysctl_perm(struct ctl_table *table, int op); |
951 | 954 | |
... | ... | @@ -1049,6 +1052,13 @@ |
1049 | 1052 | void *extra2; |
1050 | 1053 | }; |
1051 | 1054 | |
1055 | +struct ctl_table_root { | |
1056 | + struct list_head root_list; | |
1057 | + struct list_head header_list; | |
1058 | + struct list_head *(*lookup)(struct ctl_table_root *root, | |
1059 | + struct nsproxy *namespaces); | |
1060 | +}; | |
1061 | + | |
1052 | 1062 | /* struct ctl_table_header is used to maintain dynamic lists of |
1053 | 1063 | struct ctl_table trees. */ |
1054 | 1064 | struct ctl_table_header |
... | ... | @@ -1058,6 +1068,7 @@ |
1058 | 1068 | int used; |
1059 | 1069 | struct completion *unregistering; |
1060 | 1070 | struct ctl_table *ctl_table_arg; |
1071 | + struct ctl_table_root *root; | |
1061 | 1072 | }; |
1062 | 1073 | |
1063 | 1074 | /* struct ctl_path describes where in the hierarchy a table is added */ |
1064 | 1075 | |
... | ... | @@ -1066,12 +1077,16 @@ |
1066 | 1077 | int ctl_name; |
1067 | 1078 | }; |
1068 | 1079 | |
1080 | +void register_sysctl_root(struct ctl_table_root *root); | |
1081 | +struct ctl_table_header *__register_sysctl_paths( | |
1082 | + struct ctl_table_root *root, struct nsproxy *namespaces, | |
1083 | + const struct ctl_path *path, struct ctl_table *table); | |
1069 | 1084 | struct ctl_table_header *register_sysctl_table(struct ctl_table * table); |
1070 | 1085 | struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, |
1071 | 1086 | struct ctl_table *table); |
1072 | 1087 | |
1073 | 1088 | void unregister_sysctl_table(struct ctl_table_header * table); |
1074 | -int sysctl_check_table(struct ctl_table *table); | |
1089 | +int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table); | |
1075 | 1090 | |
1076 | 1091 | #else /* __KERNEL__ */ |
1077 | 1092 |
kernel/sysctl.c
... | ... | @@ -157,8 +157,16 @@ |
157 | 157 | #endif |
158 | 158 | |
159 | 159 | static struct ctl_table root_table[]; |
160 | -static struct ctl_table_header root_table_header = | |
161 | - { root_table, LIST_HEAD_INIT(root_table_header.ctl_entry) }; | |
160 | +static struct ctl_table_root sysctl_table_root; | |
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 | 171 | static struct ctl_table kern_table[]; |
164 | 172 | static struct ctl_table vm_table[]; |
165 | 173 | |
166 | 174 | |
167 | 175 | |
... | ... | @@ -1371,12 +1379,27 @@ |
1371 | 1379 | spin_unlock(&sysctl_lock); |
1372 | 1380 | } |
1373 | 1381 | |
1374 | -struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | |
1382 | +static struct list_head * | |
1383 | +lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) | |
1375 | 1384 | { |
1385 | + struct list_head *header_list; | |
1386 | + header_list = &root->header_list; | |
1387 | + if (root->lookup) | |
1388 | + header_list = root->lookup(root, namespaces); | |
1389 | + return header_list; | |
1390 | +} | |
1391 | + | |
1392 | +struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | |
1393 | + struct ctl_table_header *prev) | |
1394 | +{ | |
1395 | + struct ctl_table_root *root; | |
1396 | + struct list_head *header_list; | |
1376 | 1397 | struct ctl_table_header *head; |
1377 | 1398 | struct list_head *tmp; |
1399 | + | |
1378 | 1400 | spin_lock(&sysctl_lock); |
1379 | 1401 | if (prev) { |
1402 | + head = prev; | |
1380 | 1403 | tmp = &prev->ctl_entry; |
1381 | 1404 | unuse_table(prev); |
1382 | 1405 | goto next; |
1383 | 1406 | |
1384 | 1407 | |
1385 | 1408 | |
... | ... | @@ -1390,14 +1413,38 @@ |
1390 | 1413 | spin_unlock(&sysctl_lock); |
1391 | 1414 | return head; |
1392 | 1415 | next: |
1416 | + root = head->root; | |
1393 | 1417 | tmp = tmp->next; |
1394 | - if (tmp == &root_table_header.ctl_entry) | |
1395 | - break; | |
1418 | + header_list = lookup_header_list(root, namespaces); | |
1419 | + if (tmp != header_list) | |
1420 | + continue; | |
1421 | + | |
1422 | + do { | |
1423 | + root = list_entry(root->root_list.next, | |
1424 | + struct ctl_table_root, root_list); | |
1425 | + if (root == &sysctl_table_root) | |
1426 | + goto out; | |
1427 | + header_list = lookup_header_list(root, namespaces); | |
1428 | + } while (list_empty(header_list)); | |
1429 | + tmp = header_list->next; | |
1396 | 1430 | } |
1431 | +out: | |
1397 | 1432 | spin_unlock(&sysctl_lock); |
1398 | 1433 | return NULL; |
1399 | 1434 | } |
1400 | 1435 | |
1436 | +struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | |
1437 | +{ | |
1438 | + return __sysctl_head_next(current->nsproxy, prev); | |
1439 | +} | |
1440 | + | |
1441 | +void register_sysctl_root(struct ctl_table_root *root) | |
1442 | +{ | |
1443 | + spin_lock(&sysctl_lock); | |
1444 | + list_add_tail(&root->root_list, &sysctl_table_root.root_list); | |
1445 | + spin_unlock(&sysctl_lock); | |
1446 | +} | |
1447 | + | |
1401 | 1448 | #ifdef CONFIG_SYSCTL_SYSCALL |
1402 | 1449 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, |
1403 | 1450 | void __user *newval, size_t newlen) |
1404 | 1451 | |
... | ... | @@ -1554,14 +1601,16 @@ |
1554 | 1601 | { |
1555 | 1602 | int err; |
1556 | 1603 | sysctl_set_parent(NULL, root_table); |
1557 | - err = sysctl_check_table(root_table); | |
1604 | + err = sysctl_check_table(current->nsproxy, root_table); | |
1558 | 1605 | return 0; |
1559 | 1606 | } |
1560 | 1607 | |
1561 | 1608 | core_initcall(sysctl_init); |
1562 | 1609 | |
1563 | 1610 | /** |
1564 | - * register_sysctl_paths - register a sysctl hierarchy | |
1611 | + * __register_sysctl_paths - register a sysctl hierarchy | |
1612 | + * @root: List of sysctl headers to register on | |
1613 | + * @namespaces: Data to compute which lists of sysctl entries are visible | |
1565 | 1614 | * @path: The path to the directory the sysctl table is in. |
1566 | 1615 | * @table: the top-level table structure |
1567 | 1616 | * |
1568 | 1617 | |
... | ... | @@ -1629,9 +1678,12 @@ |
1629 | 1678 | * This routine returns %NULL on a failure to register, and a pointer |
1630 | 1679 | * to the table header on success. |
1631 | 1680 | */ |
1632 | -struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | |
1633 | - struct ctl_table *table) | |
1681 | +struct ctl_table_header *__register_sysctl_paths( | |
1682 | + struct ctl_table_root *root, | |
1683 | + struct nsproxy *namespaces, | |
1684 | + const struct ctl_path *path, struct ctl_table *table) | |
1634 | 1685 | { |
1686 | + struct list_head *header_list; | |
1635 | 1687 | struct ctl_table_header *header; |
1636 | 1688 | struct ctl_table *new, **prevp; |
1637 | 1689 | unsigned int n, npath; |
1638 | 1690 | |
1639 | 1691 | |
1640 | 1692 | |
... | ... | @@ -1674,16 +1726,35 @@ |
1674 | 1726 | INIT_LIST_HEAD(&header->ctl_entry); |
1675 | 1727 | header->used = 0; |
1676 | 1728 | header->unregistering = NULL; |
1729 | + header->root = root; | |
1677 | 1730 | sysctl_set_parent(NULL, header->ctl_table); |
1678 | - if (sysctl_check_table(header->ctl_table)) { | |
1731 | + if (sysctl_check_table(namespaces, header->ctl_table)) { | |
1679 | 1732 | kfree(header); |
1680 | 1733 | return NULL; |
1681 | 1734 | } |
1682 | 1735 | spin_lock(&sysctl_lock); |
1683 | - list_add_tail(&header->ctl_entry, &root_table_header.ctl_entry); | |
1736 | + header_list = lookup_header_list(root, namespaces); | |
1737 | + list_add_tail(&header->ctl_entry, header_list); | |
1684 | 1738 | spin_unlock(&sysctl_lock); |
1685 | 1739 | |
1686 | 1740 | return header; |
1741 | +} | |
1742 | + | |
1743 | +/** | |
1744 | + * register_sysctl_table_path - register a sysctl table hierarchy | |
1745 | + * @path: The path to the directory the sysctl table is in. | |
1746 | + * @table: the top-level table structure | |
1747 | + * | |
1748 | + * Register a sysctl table hierarchy. @table should be a filled in ctl_table | |
1749 | + * array. A completely 0 filled entry terminates the table. | |
1750 | + * | |
1751 | + * See __register_sysctl_paths for more details. | |
1752 | + */ | |
1753 | +struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | |
1754 | + struct ctl_table *table) | |
1755 | +{ | |
1756 | + return __register_sysctl_paths(&sysctl_table_root, current->nsproxy, | |
1757 | + path, table); | |
1687 | 1758 | } |
1688 | 1759 | |
1689 | 1760 | /** |
kernel/sysctl_check.c
... | ... | @@ -1342,7 +1342,8 @@ |
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 | 1348 | struct ctl_table_header *head; |
1348 | 1349 | struct ctl_table *ref, *test; |
... | ... | @@ -1350,8 +1351,8 @@ |
1350 | 1351 | |
1351 | 1352 | depth = sysctl_depth(table); |
1352 | 1353 | |
1353 | - for (head = sysctl_head_next(NULL); head; | |
1354 | - head = sysctl_head_next(head)) { | |
1354 | + for (head = __sysctl_head_next(namespaces, NULL); head; | |
1355 | + head = __sysctl_head_next(namespaces, head)) { | |
1355 | 1356 | cur_depth = depth; |
1356 | 1357 | ref = head->ctl_table; |
1357 | 1358 | repeat: |
1358 | 1359 | |
... | ... | @@ -1396,13 +1397,14 @@ |
1396 | 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 | 1403 | struct ctl_table *ref; |
1402 | 1404 | int error; |
1403 | 1405 | |
1404 | 1406 | error = 0; |
1405 | - ref = sysctl_check_lookup(table); | |
1407 | + ref = sysctl_check_lookup(namespaces, table); | |
1406 | 1408 | if (ref) { |
1407 | 1409 | int match = 0; |
1408 | 1410 | if ((!table->procname && !ref->procname) || |
1409 | 1411 | |
... | ... | @@ -1427,11 +1429,12 @@ |
1427 | 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 | 1435 | struct ctl_table *ref; |
1433 | 1436 | |
1434 | - ref = sysctl_check_lookup(table); | |
1437 | + ref = sysctl_check_lookup(namespaces, table); | |
1435 | 1438 | if (ref && (ref != table)) |
1436 | 1439 | set_fail(fail, table, "Sysctl already exists"); |
1437 | 1440 | } |
... | ... | @@ -1455,7 +1458,7 @@ |
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 | 1463 | int error = 0; |
1461 | 1464 | for (; table->ctl_name || table->procname; table++) { |
... | ... | @@ -1485,7 +1488,7 @@ |
1485 | 1488 | set_fail(&fail, table, "Directory with extra1"); |
1486 | 1489 | if (table->extra2) |
1487 | 1490 | set_fail(&fail, table, "Directory with extra2"); |
1488 | - if (sysctl_check_dir(table)) | |
1491 | + if (sysctl_check_dir(namespaces, table)) | |
1489 | 1492 | set_fail(&fail, table, "Inconsistent directory names"); |
1490 | 1493 | } else { |
1491 | 1494 | if ((table->strategy == sysctl_data) || |
... | ... | @@ -1534,7 +1537,7 @@ |
1534 | 1537 | if (!table->procname && table->proc_handler) |
1535 | 1538 | set_fail(&fail, table, "proc_handler without procname"); |
1536 | 1539 | #endif |
1537 | - sysctl_check_leaf(table, &fail); | |
1540 | + sysctl_check_leaf(namespaces, table, &fail); | |
1538 | 1541 | } |
1539 | 1542 | sysctl_check_bin_path(table, &fail); |
1540 | 1543 | if (fail) { |
... | ... | @@ -1542,7 +1545,7 @@ |
1542 | 1545 | error = -EINVAL; |
1543 | 1546 | } |
1544 | 1547 | if (table->child) |
1545 | - error |= sysctl_check_table(table->child); | |
1548 | + error |= sysctl_check_table(namespaces, table->child); | |
1546 | 1549 | } |
1547 | 1550 | return error; |
1548 | 1551 | } |