diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2012-01-22 21:22:05 -0500 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2012-01-24 19:40:27 -0500 |
commit | f728019bb72e655680c02ad1829323054a8e875f (patch) | |
tree | cc1915e06b806004c662abb0f0cb4f897111be1c /fs/proc/proc_sysctl.c | |
parent | ec6a52668d0bbc6d648e978c327150254bf1ce7f (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/proc/proc_sysctl.c')
-rw-r--r-- | fs/proc/proc_sysctl.c | 165 |
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 | ||
1052 | static 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 | |||
1071 | static 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; | ||
1141 | out: | ||
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 | |||
1103 | out: | 1204 | out: |
1104 | kfree(new_path); | 1205 | kfree(new_path); |
1105 | return header; | 1206 | return header; |
1207 | |||
1208 | err_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 | */ |
1150 | void unregister_sysctl_table(struct ctl_table_header * header) | 1262 | void 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) { |