diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index d58a958444a..095248082b7 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -226,6 +226,12 @@ struct cftype { */ int (*read_map) (struct cgroup *cont, struct cftype *cft, struct cgroup_map_cb *cb); + /* + * read_seq_string() is used for outputting a simple sequence + * using seqfile. + */ + int (*read_seq_string) (struct cgroup *cont, struct cftype *cft, + struct seq_file *m); ssize_t (*write) (struct cgroup *cgrp, struct cftype *cft, struct file *file, diff --git a/kernel/cgroup.c b/kernel/cgroup.c index aeceb886898..abc433772e5 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1549,11 +1549,14 @@ static int cgroup_seqfile_show(struct seq_file *m, void *arg) { struct cgroup_seqfile_state *state = m->private; struct cftype *cft = state->cft; - struct cgroup_map_cb cb = { - .fill = cgroup_map_add, - .state = m, - }; - return cft->read_map(state->cgroup, cft, &cb); + if (cft->read_map) { + struct cgroup_map_cb cb = { + .fill = cgroup_map_add, + .state = m, + }; + return cft->read_map(state->cgroup, cft, &cb); + } + return cft->read_seq_string(state->cgroup, cft, m); } 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) cft = __d_cft(file->f_dentry); if (!cft) return -ENODEV; - if (cft->read_map) { + if (cft->read_map || cft->read_seq_string) { struct cgroup_seqfile_state *state = kzalloc(sizeof(*state), GFP_USER); if (!state) diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 4237b19e8fb..4ea583689ee 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -9,6 +9,7 @@ #include #include #include +#include #define ACC_MKNOD 1 #define ACC_READ 2 @@ -201,11 +202,15 @@ static void devcgroup_destroy(struct cgroup_subsys *ss, #define DEVCG_ALLOW 1 #define DEVCG_DENY 2 +#define DEVCG_LIST 3 + +#define MAJMINLEN 10 +#define ACCLEN 4 static void set_access(char *acc, short access) { int idx = 0; - memset(acc, 0, 4); + memset(acc, 0, ACCLEN); if (access & ACC_READ) acc[idx++] = 'r'; if (access & ACC_WRITE) @@ -225,70 +230,33 @@ static char type_to_char(short type) return 'X'; } -static void set_majmin(char *str, int len, unsigned m) +static void set_majmin(char *str, unsigned m) { - memset(str, 0, len); + memset(str, 0, MAJMINLEN); if (m == ~0) sprintf(str, "*"); else - snprintf(str, len, "%d", m); + snprintf(str, MAJMINLEN, "%d", m); } -static char *print_whitelist(struct dev_cgroup *devcgroup, int *len) +static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, + struct seq_file *m) { - char *buf, *s, acc[4]; + struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup); struct dev_whitelist_item *wh; - int ret; - int count = 0; - char maj[10], min[10]; - - buf = kmalloc(4096, GFP_KERNEL); - if (!buf) - return ERR_PTR(-ENOMEM); - s = buf; - *s = '\0'; - *len = 0; + char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; spin_lock(&devcgroup->lock); list_for_each_entry(wh, &devcgroup->whitelist, list) { set_access(acc, wh->access); - set_majmin(maj, 10, wh->major); - set_majmin(min, 10, wh->minor); - ret = snprintf(s, 4095-(s-buf), "%c %s:%s %s\n", - type_to_char(wh->type), maj, min, acc); - if (s+ret >= buf+4095) { - kfree(buf); - buf = ERR_PTR(-ENOMEM); - break; - } - s += ret; - *len += ret; - count++; + set_majmin(maj, wh->major); + set_majmin(min, wh->minor); + seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type), + maj, min, acc); } spin_unlock(&devcgroup->lock); - return buf; -} - -static ssize_t devcgroup_access_read(struct cgroup *cgroup, - struct cftype *cft, struct file *file, - char __user *userbuf, size_t nbytes, loff_t *ppos) -{ - struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup); - int filetype = cft->private; - char *buffer; - int uninitialized_var(len); - int retval; - - if (filetype != DEVCG_ALLOW) - return -EINVAL; - buffer = print_whitelist(devcgroup, &len); - if (IS_ERR(buffer)) - return PTR_ERR(buffer); - - retval = simple_read_from_buffer(userbuf, nbytes, ppos, buffer, len); - kfree(buffer); - return retval; + return 0; } /* @@ -501,7 +469,6 @@ out1: static struct cftype dev_cgroup_files[] = { { .name = "allow", - .read = devcgroup_access_read, .write = devcgroup_access_write, .private = DEVCG_ALLOW, }, @@ -510,6 +477,11 @@ static struct cftype dev_cgroup_files[] = { .write = devcgroup_access_write, .private = DEVCG_DENY, }, + { + .name = "list", + .read_seq_string = devcgroup_seq_read, + .private = DEVCG_LIST, + }, }; static int devcgroup_populate(struct cgroup_subsys *ss,