diff options
Diffstat (limited to 'security/device_cgroup.c')
-rw-r--r-- | security/device_cgroup.c | 74 |
1 files changed, 23 insertions, 51 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 4237b19e8fb3..4ea583689eec 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/ctype.h> | 9 | #include <linux/ctype.h> |
10 | #include <linux/list.h> | 10 | #include <linux/list.h> |
11 | #include <linux/uaccess.h> | 11 | #include <linux/uaccess.h> |
12 | #include <linux/seq_file.h> | ||
12 | 13 | ||
13 | #define ACC_MKNOD 1 | 14 | #define ACC_MKNOD 1 |
14 | #define ACC_READ 2 | 15 | #define ACC_READ 2 |
@@ -201,11 +202,15 @@ static void devcgroup_destroy(struct cgroup_subsys *ss, | |||
201 | 202 | ||
202 | #define DEVCG_ALLOW 1 | 203 | #define DEVCG_ALLOW 1 |
203 | #define DEVCG_DENY 2 | 204 | #define DEVCG_DENY 2 |
205 | #define DEVCG_LIST 3 | ||
206 | |||
207 | #define MAJMINLEN 10 | ||
208 | #define ACCLEN 4 | ||
204 | 209 | ||
205 | static void set_access(char *acc, short access) | 210 | static void set_access(char *acc, short access) |
206 | { | 211 | { |
207 | int idx = 0; | 212 | int idx = 0; |
208 | memset(acc, 0, 4); | 213 | memset(acc, 0, ACCLEN); |
209 | if (access & ACC_READ) | 214 | if (access & ACC_READ) |
210 | acc[idx++] = 'r'; | 215 | acc[idx++] = 'r'; |
211 | if (access & ACC_WRITE) | 216 | if (access & ACC_WRITE) |
@@ -225,70 +230,33 @@ static char type_to_char(short type) | |||
225 | return 'X'; | 230 | return 'X'; |
226 | } | 231 | } |
227 | 232 | ||
228 | static void set_majmin(char *str, int len, unsigned m) | 233 | static void set_majmin(char *str, unsigned m) |
229 | { | 234 | { |
230 | memset(str, 0, len); | 235 | memset(str, 0, MAJMINLEN); |
231 | if (m == ~0) | 236 | if (m == ~0) |
232 | sprintf(str, "*"); | 237 | sprintf(str, "*"); |
233 | else | 238 | else |
234 | snprintf(str, len, "%d", m); | 239 | snprintf(str, MAJMINLEN, "%d", m); |
235 | } | 240 | } |
236 | 241 | ||
237 | static char *print_whitelist(struct dev_cgroup *devcgroup, int *len) | 242 | static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, |
243 | struct seq_file *m) | ||
238 | { | 244 | { |
239 | char *buf, *s, acc[4]; | 245 | struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup); |
240 | struct dev_whitelist_item *wh; | 246 | struct dev_whitelist_item *wh; |
241 | int ret; | 247 | char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; |
242 | int count = 0; | ||
243 | char maj[10], min[10]; | ||
244 | |||
245 | buf = kmalloc(4096, GFP_KERNEL); | ||
246 | if (!buf) | ||
247 | return ERR_PTR(-ENOMEM); | ||
248 | s = buf; | ||
249 | *s = '\0'; | ||
250 | *len = 0; | ||
251 | 248 | ||
252 | spin_lock(&devcgroup->lock); | 249 | spin_lock(&devcgroup->lock); |
253 | list_for_each_entry(wh, &devcgroup->whitelist, list) { | 250 | list_for_each_entry(wh, &devcgroup->whitelist, list) { |
254 | set_access(acc, wh->access); | 251 | set_access(acc, wh->access); |
255 | set_majmin(maj, 10, wh->major); | 252 | set_majmin(maj, wh->major); |
256 | set_majmin(min, 10, wh->minor); | 253 | set_majmin(min, wh->minor); |
257 | ret = snprintf(s, 4095-(s-buf), "%c %s:%s %s\n", | 254 | seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type), |
258 | type_to_char(wh->type), maj, min, acc); | 255 | maj, min, acc); |
259 | if (s+ret >= buf+4095) { | ||
260 | kfree(buf); | ||
261 | buf = ERR_PTR(-ENOMEM); | ||
262 | break; | ||
263 | } | ||
264 | s += ret; | ||
265 | *len += ret; | ||
266 | count++; | ||
267 | } | 256 | } |
268 | spin_unlock(&devcgroup->lock); | 257 | spin_unlock(&devcgroup->lock); |
269 | 258 | ||
270 | return buf; | 259 | return 0; |
271 | } | ||
272 | |||
273 | static ssize_t devcgroup_access_read(struct cgroup *cgroup, | ||
274 | struct cftype *cft, struct file *file, | ||
275 | char __user *userbuf, size_t nbytes, loff_t *ppos) | ||
276 | { | ||
277 | struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup); | ||
278 | int filetype = cft->private; | ||
279 | char *buffer; | ||
280 | int uninitialized_var(len); | ||
281 | int retval; | ||
282 | |||
283 | if (filetype != DEVCG_ALLOW) | ||
284 | return -EINVAL; | ||
285 | buffer = print_whitelist(devcgroup, &len); | ||
286 | if (IS_ERR(buffer)) | ||
287 | return PTR_ERR(buffer); | ||
288 | |||
289 | retval = simple_read_from_buffer(userbuf, nbytes, ppos, buffer, len); | ||
290 | kfree(buffer); | ||
291 | return retval; | ||
292 | } | 260 | } |
293 | 261 | ||
294 | /* | 262 | /* |
@@ -501,7 +469,6 @@ out1: | |||
501 | static struct cftype dev_cgroup_files[] = { | 469 | static struct cftype dev_cgroup_files[] = { |
502 | { | 470 | { |
503 | .name = "allow", | 471 | .name = "allow", |
504 | .read = devcgroup_access_read, | ||
505 | .write = devcgroup_access_write, | 472 | .write = devcgroup_access_write, |
506 | .private = DEVCG_ALLOW, | 473 | .private = DEVCG_ALLOW, |
507 | }, | 474 | }, |
@@ -510,6 +477,11 @@ static struct cftype dev_cgroup_files[] = { | |||
510 | .write = devcgroup_access_write, | 477 | .write = devcgroup_access_write, |
511 | .private = DEVCG_DENY, | 478 | .private = DEVCG_DENY, |
512 | }, | 479 | }, |
480 | { | ||
481 | .name = "list", | ||
482 | .read_seq_string = devcgroup_seq_read, | ||
483 | .private = DEVCG_LIST, | ||
484 | }, | ||
513 | }; | 485 | }; |
514 | 486 | ||
515 | static int devcgroup_populate(struct cgroup_subsys *ss, | 487 | static int devcgroup_populate(struct cgroup_subsys *ss, |