diff options
-rw-r--r-- | include/linux/cgroup.h | 6 | ||||
-rw-r--r-- | kernel/cgroup.c | 15 | ||||
-rw-r--r-- | security/device_cgroup.c | 74 |
3 files changed, 38 insertions, 57 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index d58a958444ab..095248082b7e 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h | |||
@@ -226,6 +226,12 @@ struct cftype { | |||
226 | */ | 226 | */ |
227 | int (*read_map) (struct cgroup *cont, struct cftype *cft, | 227 | int (*read_map) (struct cgroup *cont, struct cftype *cft, |
228 | struct cgroup_map_cb *cb); | 228 | struct cgroup_map_cb *cb); |
229 | /* | ||
230 | * read_seq_string() is used for outputting a simple sequence | ||
231 | * using seqfile. | ||
232 | */ | ||
233 | int (*read_seq_string) (struct cgroup *cont, struct cftype *cft, | ||
234 | struct seq_file *m); | ||
229 | 235 | ||
230 | ssize_t (*write) (struct cgroup *cgrp, struct cftype *cft, | 236 | ssize_t (*write) (struct cgroup *cgrp, struct cftype *cft, |
231 | struct file *file, | 237 | struct file *file, |
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index aeceb8868981..abc433772e5a 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -1549,11 +1549,14 @@ static int cgroup_seqfile_show(struct seq_file *m, void *arg) | |||
1549 | { | 1549 | { |
1550 | struct cgroup_seqfile_state *state = m->private; | 1550 | struct cgroup_seqfile_state *state = m->private; |
1551 | struct cftype *cft = state->cft; | 1551 | struct cftype *cft = state->cft; |
1552 | struct cgroup_map_cb cb = { | 1552 | if (cft->read_map) { |
1553 | .fill = cgroup_map_add, | 1553 | struct cgroup_map_cb cb = { |
1554 | .state = m, | 1554 | .fill = cgroup_map_add, |
1555 | }; | 1555 | .state = m, |
1556 | return cft->read_map(state->cgroup, cft, &cb); | 1556 | }; |
1557 | return cft->read_map(state->cgroup, cft, &cb); | ||
1558 | } | ||
1559 | return cft->read_seq_string(state->cgroup, cft, m); | ||
1557 | } | 1560 | } |
1558 | 1561 | ||
1559 | int cgroup_seqfile_release(struct inode *inode, struct file *file) | 1562 | int cgroup_seqfile_release(struct inode *inode, struct file *file) |
@@ -1581,7 +1584,7 @@ static int cgroup_file_open(struct inode *inode, struct file *file) | |||
1581 | cft = __d_cft(file->f_dentry); | 1584 | cft = __d_cft(file->f_dentry); |
1582 | if (!cft) | 1585 | if (!cft) |
1583 | return -ENODEV; | 1586 | return -ENODEV; |
1584 | if (cft->read_map) { | 1587 | if (cft->read_map || cft->read_seq_string) { |
1585 | struct cgroup_seqfile_state *state = | 1588 | struct cgroup_seqfile_state *state = |
1586 | kzalloc(sizeof(*state), GFP_USER); | 1589 | kzalloc(sizeof(*state), GFP_USER); |
1587 | if (!state) | 1590 | if (!state) |
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, |