aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2012-01-22 21:22:05 -0500
committerEric W. Biederman <ebiederm@xmission.com>2012-01-24 19:40:27 -0500
commitf728019bb72e655680c02ad1829323054a8e875f (patch)
treecc1915e06b806004c662abb0f0cb4f897111be1c /fs
parentec6a52668d0bbc6d648e978c327150254bf1ce7f (diff)
sysctl: register only tables of sysctl files
Split the registration of a complex ctl_table array which may have arbitrary numbers of directories (->child != NULL) and tables of files into a series of simpler registrations that only register tables of files. Graphically: register('dir', { + file-a + file-b + subdir1 + file-c + subdir2 + file-d + file-e }) is transformed into: wrapper->subheaders[0] = register('dir', {file1-a, file1-b}) wrapper->subheaders[1] = register('dir/subdir1', {file-c}) wrapper->subheaders[2] = register('dir/subdir2', {file-d, file-e}) return wrapper This guarantees that __register_sysctl_table will only see a simple ctl_table array with all entries having (->child == NULL). Care was taken to pass the original simple ctl_table arrays to __register_sysctl_table whenever possible. This change is derived from a similar patch written by Lucrian Grijincu. Inspired-by: Lucian Adrian Grijincu <lucian.grijincu@gmail.com> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/proc/proc_sysctl.c165
1 files changed, 147 insertions, 18 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 9b91deeeb56c..6bab2ae9e395 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -882,7 +882,7 @@ static int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *tabl
882#endif /* CONFIG_SYSCTL_SYSCALL_CHECK */ 882#endif /* CONFIG_SYSCTL_SYSCALL_CHECK */
883 883
884/** 884/**
885 * __register_sysctl_table - register a sysctl table 885 * __register_sysctl_table - register a leaf sysctl table
886 * @root: List of sysctl headers to register on 886 * @root: List of sysctl headers to register on
887 * @namespaces: Data to compute which lists of sysctl entries are visible 887 * @namespaces: Data to compute which lists of sysctl entries are visible
888 * @path: The path to the directory the sysctl table is in. 888 * @path: The path to the directory the sysctl table is in.
@@ -900,29 +900,19 @@ static int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *tabl
900 * 900 *
901 * maxlen - the maximum size in bytes of the data 901 * maxlen - the maximum size in bytes of the data
902 * 902 *
903 * mode - the file permissions for the /proc/sys file, and for sysctl(2) 903 * mode - the file permissions for the /proc/sys file
904 * 904 *
905 * child - a pointer to the child sysctl table if this entry is a directory, or 905 * child - must be %NULL.
906 * %NULL.
907 * 906 *
908 * proc_handler - the text handler routine (described below) 907 * proc_handler - the text handler routine (described below)
909 * 908 *
910 * de - for internal use by the sysctl routines
911 *
912 * extra1, extra2 - extra pointers usable by the proc handler routines 909 * extra1, extra2 - extra pointers usable by the proc handler routines
913 * 910 *
914 * Leaf nodes in the sysctl tree will be represented by a single file 911 * Leaf nodes in the sysctl tree will be represented by a single file
915 * under /proc; non-leaf nodes will be represented by directories. 912 * under /proc; non-leaf nodes will be represented by directories.
916 * 913 *
917 * sysctl(2) can automatically manage read and write requests through 914 * There must be a proc_handler routine for any terminal nodes.
918 * the sysctl table. The data and maxlen fields of the ctl_table 915 * Several default handlers are available to cover common cases -
919 * struct enable minimal validation of the values being written to be
920 * performed, and the mode field allows minimal authentication.
921 *
922 * There must be a proc_handler routine for any terminal nodes
923 * mirrored under /proc/sys (non-terminals are handled by a built-in
924 * directory handler). Several default handlers are available to
925 * cover common cases -
926 * 916 *
927 * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(), 917 * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(),
928 * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(), 918 * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(),
@@ -1059,6 +1049,100 @@ static char *append_path(const char *path, char *pos, const char *name)
1059 return pos; 1049 return pos;
1060} 1050}
1061 1051
1052static int count_subheaders(struct ctl_table *table)
1053{
1054 int has_files = 0;
1055 int nr_subheaders = 0;
1056 struct ctl_table *entry;
1057
1058 /* special case: no directory and empty directory */
1059 if (!table || !table->procname)
1060 return 1;
1061
1062 for (entry = table; entry->procname; entry++) {
1063 if (entry->child)
1064 nr_subheaders += count_subheaders(entry->child);
1065 else
1066 has_files = 1;
1067 }
1068 return nr_subheaders + has_files;
1069}
1070
1071static int register_leaf_sysctl_tables(const char *path, char *pos,
1072 struct ctl_table_header ***subheader,
1073 struct ctl_table_root *root, struct nsproxy *namespaces,
1074 struct ctl_table *table)
1075{
1076 struct ctl_table *ctl_table_arg = NULL;
1077 struct ctl_table *entry, *files;
1078 int nr_files = 0;
1079 int nr_dirs = 0;
1080 int err = -ENOMEM;
1081
1082 for (entry = table; entry->procname; entry++) {
1083 if (entry->child)
1084 nr_dirs++;
1085 else
1086 nr_files++;
1087 }
1088
1089 files = table;
1090 /* If there are mixed files and directories we need a new table */
1091 if (nr_dirs && nr_files) {
1092 struct ctl_table *new;
1093 files = kzalloc(sizeof(struct ctl_table) * (nr_files + 1),
1094 GFP_KERNEL);
1095 if (!files)
1096 goto out;
1097
1098 ctl_table_arg = files;
1099 for (new = files, entry = table; entry->procname; entry++) {
1100 if (entry->child)
1101 continue;
1102 *new = *entry;
1103 new++;
1104 }
1105 }
1106
1107 /* Register everything except a directory full of subdirectories */
1108 if (nr_files || !nr_dirs) {
1109 struct ctl_table_header *header;
1110 header = __register_sysctl_table(root, namespaces, path, files);
1111 if (!header) {
1112 kfree(ctl_table_arg);
1113 goto out;
1114 }
1115
1116 /* Remember if we need to free the file table */
1117 header->ctl_table_arg = ctl_table_arg;
1118 **subheader = header;
1119 (*subheader)++;
1120 }
1121
1122 /* Recurse into the subdirectories. */
1123 for (entry = table; entry->procname; entry++) {
1124 char *child_pos;
1125
1126 if (!entry->child)
1127 continue;
1128
1129 err = -ENAMETOOLONG;
1130 child_pos = append_path(path, pos, entry->procname);
1131 if (!child_pos)
1132 goto out;
1133
1134 err = register_leaf_sysctl_tables(path, child_pos, subheader,
1135 root, namespaces, entry->child);
1136 pos[0] = '\0';
1137 if (err)
1138 goto out;
1139 }
1140 err = 0;
1141out:
1142 /* On failure our caller will unregister all registered subheaders */
1143 return err;
1144}
1145
1062/** 1146/**
1063 * __register_sysctl_paths - register a sysctl table hierarchy 1147 * __register_sysctl_paths - register a sysctl table hierarchy
1064 * @root: List of sysctl headers to register on 1148 * @root: List of sysctl headers to register on
@@ -1077,7 +1161,8 @@ struct ctl_table_header *__register_sysctl_paths(
1077 const struct ctl_path *path, struct ctl_table *table) 1161 const struct ctl_path *path, struct ctl_table *table)
1078{ 1162{
1079 struct ctl_table *ctl_table_arg = table; 1163 struct ctl_table *ctl_table_arg = table;
1080 struct ctl_table_header *header = NULL; 1164 int nr_subheaders = count_subheaders(table);
1165 struct ctl_table_header *header = NULL, **subheaders, **subheader;
1081 const struct ctl_path *component; 1166 const struct ctl_path *component;
1082 char *new_path, *pos; 1167 char *new_path, *pos;
1083 1168
@@ -1097,12 +1182,39 @@ struct ctl_table_header *__register_sysctl_paths(
1097 goto out; 1182 goto out;
1098 table = table->child; 1183 table = table->child;
1099 } 1184 }
1100 header = __register_sysctl_table(root, namespaces, new_path, table); 1185 if (nr_subheaders == 1) {
1101 if (header) 1186 header = __register_sysctl_table(root, namespaces, new_path, table);
1187 if (header)
1188 header->ctl_table_arg = ctl_table_arg;
1189 } else {
1190 header = kzalloc(sizeof(*header) +
1191 sizeof(*subheaders)*nr_subheaders, GFP_KERNEL);
1192 if (!header)
1193 goto out;
1194
1195 subheaders = (struct ctl_table_header **) (header + 1);
1196 subheader = subheaders;
1102 header->ctl_table_arg = ctl_table_arg; 1197 header->ctl_table_arg = ctl_table_arg;
1198
1199 if (register_leaf_sysctl_tables(new_path, pos, &subheader,
1200 root, namespaces, table))
1201 goto err_register_leaves;
1202 }
1203
1103out: 1204out:
1104 kfree(new_path); 1205 kfree(new_path);
1105 return header; 1206 return header;
1207
1208err_register_leaves:
1209 while (subheader > subheaders) {
1210 struct ctl_table_header *subh = *(--subheader);
1211 struct ctl_table *table = subh->ctl_table_arg;
1212 unregister_sysctl_table(subh);
1213 kfree(table);
1214 }
1215 kfree(header);
1216 header = NULL;
1217 goto out;
1106} 1218}
1107 1219
1108/** 1220/**
@@ -1149,11 +1261,28 @@ EXPORT_SYMBOL(register_sysctl_table);
1149 */ 1261 */
1150void unregister_sysctl_table(struct ctl_table_header * header) 1262void unregister_sysctl_table(struct ctl_table_header * header)
1151{ 1263{
1264 int nr_subheaders;
1152 might_sleep(); 1265 might_sleep();
1153 1266
1154 if (header == NULL) 1267 if (header == NULL)
1155 return; 1268 return;
1156 1269
1270 nr_subheaders = count_subheaders(header->ctl_table_arg);
1271 if (unlikely(nr_subheaders > 1)) {
1272 struct ctl_table_header **subheaders;
1273 int i;
1274
1275 subheaders = (struct ctl_table_header **)(header + 1);
1276 for (i = nr_subheaders -1; i >= 0; i--) {
1277 struct ctl_table_header *subh = subheaders[i];
1278 struct ctl_table *table = subh->ctl_table_arg;
1279 unregister_sysctl_table(subh);
1280 kfree(table);
1281 }
1282 kfree(header);
1283 return;
1284 }
1285
1157 spin_lock(&sysctl_lock); 1286 spin_lock(&sysctl_lock);
1158 start_unregistering(header); 1287 start_unregistering(header);
1159 if (!--header->parent->count) { 1288 if (!--header->parent->count) {