cgroup: unexport cgroup_css()
[linux-2.6-block.git] / security / device_cgroup.c
CommitLineData
08ce5f16 1/*
47c59803 2 * device_cgroup.c - device cgroup subsystem
08ce5f16
SH
3 *
4 * Copyright 2007 IBM Corp
5 */
6
7#include <linux/device_cgroup.h>
8#include <linux/cgroup.h>
9#include <linux/ctype.h>
10#include <linux/list.h>
11#include <linux/uaccess.h>
29486df3 12#include <linux/seq_file.h>
5a0e3ad6 13#include <linux/slab.h>
47c59803 14#include <linux/rcupdate.h>
b4046f00 15#include <linux/mutex.h>
08ce5f16
SH
16
17#define ACC_MKNOD 1
18#define ACC_READ 2
19#define ACC_WRITE 4
20#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
21
22#define DEV_BLOCK 1
23#define DEV_CHAR 2
24#define DEV_ALL 4 /* this represents all devices */
25
b4046f00
LZ
26static DEFINE_MUTEX(devcgroup_mutex);
27
c39a2a30
AR
28enum devcg_behavior {
29 DEVCG_DEFAULT_NONE,
30 DEVCG_DEFAULT_ALLOW,
31 DEVCG_DEFAULT_DENY,
32};
33
08ce5f16 34/*
db9aeca9 35 * exception list locking rules:
b4046f00 36 * hold devcgroup_mutex for update/read.
47c59803 37 * hold rcu_read_lock() for read.
08ce5f16
SH
38 */
39
db9aeca9 40struct dev_exception_item {
08ce5f16
SH
41 u32 major, minor;
42 short type;
43 short access;
44 struct list_head list;
4efd1a1b 45 struct rcu_head rcu;
08ce5f16
SH
46};
47
48struct dev_cgroup {
49 struct cgroup_subsys_state css;
db9aeca9 50 struct list_head exceptions;
c39a2a30 51 enum devcg_behavior behavior;
08ce5f16
SH
52};
53
b66862f7
PE
54static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
55{
a7c6d554 56 return s ? container_of(s, struct dev_cgroup, css) : NULL;
b66862f7
PE
57}
58
f92523e3
PM
59static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
60{
8af01f56 61 return css_to_devcgroup(task_css(task, devices_subsys_id));
f92523e3
PM
62}
63
08ce5f16
SH
64struct cgroup_subsys devices_subsys;
65
eb95419b 66static int devcgroup_can_attach(struct cgroup_subsys_state *new_css,
761b3ef5 67 struct cgroup_taskset *set)
08ce5f16 68{
2f7ee569 69 struct task_struct *task = cgroup_taskset_first(set);
08ce5f16 70
2f7ee569
TH
71 if (current != task && !capable(CAP_SYS_ADMIN))
72 return -EPERM;
08ce5f16
SH
73 return 0;
74}
75
76/*
b4046f00 77 * called under devcgroup_mutex
08ce5f16 78 */
db9aeca9 79static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig)
08ce5f16 80{
db9aeca9 81 struct dev_exception_item *ex, *tmp, *new;
08ce5f16 82
4b1c7840
TH
83 lockdep_assert_held(&devcgroup_mutex);
84
db9aeca9
AR
85 list_for_each_entry(ex, orig, list) {
86 new = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
08ce5f16
SH
87 if (!new)
88 goto free_and_exit;
08ce5f16
SH
89 list_add_tail(&new->list, dest);
90 }
91
92 return 0;
93
94free_and_exit:
db9aeca9
AR
95 list_for_each_entry_safe(ex, tmp, dest, list) {
96 list_del(&ex->list);
97 kfree(ex);
08ce5f16
SH
98 }
99 return -ENOMEM;
100}
101
08ce5f16 102/*
b4046f00 103 * called under devcgroup_mutex
08ce5f16 104 */
db9aeca9
AR
105static int dev_exception_add(struct dev_cgroup *dev_cgroup,
106 struct dev_exception_item *ex)
08ce5f16 107{
db9aeca9 108 struct dev_exception_item *excopy, *walk;
08ce5f16 109
4b1c7840
TH
110 lockdep_assert_held(&devcgroup_mutex);
111
db9aeca9
AR
112 excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
113 if (!excopy)
08ce5f16
SH
114 return -ENOMEM;
115
db9aeca9
AR
116 list_for_each_entry(walk, &dev_cgroup->exceptions, list) {
117 if (walk->type != ex->type)
d1ee2971 118 continue;
db9aeca9 119 if (walk->major != ex->major)
d1ee2971 120 continue;
db9aeca9 121 if (walk->minor != ex->minor)
d1ee2971
PE
122 continue;
123
db9aeca9
AR
124 walk->access |= ex->access;
125 kfree(excopy);
126 excopy = NULL;
d1ee2971
PE
127 }
128
db9aeca9
AR
129 if (excopy != NULL)
130 list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions);
08ce5f16
SH
131 return 0;
132}
133
134/*
b4046f00 135 * called under devcgroup_mutex
08ce5f16 136 */
db9aeca9
AR
137static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
138 struct dev_exception_item *ex)
08ce5f16 139{
db9aeca9 140 struct dev_exception_item *walk, *tmp;
08ce5f16 141
4b1c7840
TH
142 lockdep_assert_held(&devcgroup_mutex);
143
db9aeca9
AR
144 list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) {
145 if (walk->type != ex->type)
08ce5f16 146 continue;
db9aeca9 147 if (walk->major != ex->major)
08ce5f16 148 continue;
db9aeca9 149 if (walk->minor != ex->minor)
08ce5f16
SH
150 continue;
151
db9aeca9 152 walk->access &= ~ex->access;
08ce5f16 153 if (!walk->access) {
4efd1a1b 154 list_del_rcu(&walk->list);
6034f7e6 155 kfree_rcu(walk, rcu);
08ce5f16
SH
156 }
157 }
08ce5f16
SH
158}
159
53eb8c82
JS
160static void __dev_exception_clean(struct dev_cgroup *dev_cgroup)
161{
162 struct dev_exception_item *ex, *tmp;
163
164 list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {
165 list_del_rcu(&ex->list);
166 kfree_rcu(ex, rcu);
167 }
168}
169
868539a3 170/**
db9aeca9
AR
171 * dev_exception_clean - frees all entries of the exception list
172 * @dev_cgroup: dev_cgroup with the exception list to be cleaned
868539a3
AR
173 *
174 * called under devcgroup_mutex
175 */
db9aeca9 176static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
868539a3 177{
4b1c7840
TH
178 lockdep_assert_held(&devcgroup_mutex);
179
53eb8c82 180 __dev_exception_clean(dev_cgroup);
868539a3
AR
181}
182
bd2953eb
AR
183static inline bool is_devcg_online(const struct dev_cgroup *devcg)
184{
185 return (devcg->behavior != DEVCG_DEFAULT_NONE);
186}
187
1909554c
AR
188/**
189 * devcgroup_online - initializes devcgroup's behavior and exceptions based on
190 * parent's
eb95419b 191 * @css: css getting online
1909554c
AR
192 * returns 0 in case of success, error code otherwise
193 */
eb95419b 194static int devcgroup_online(struct cgroup_subsys_state *css)
1909554c 195{
eb95419b
TH
196 struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
197 struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(css));
1909554c
AR
198 int ret = 0;
199
200 mutex_lock(&devcgroup_mutex);
1909554c
AR
201
202 if (parent_dev_cgroup == NULL)
203 dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
204 else {
205 ret = dev_exceptions_copy(&dev_cgroup->exceptions,
206 &parent_dev_cgroup->exceptions);
207 if (!ret)
208 dev_cgroup->behavior = parent_dev_cgroup->behavior;
209 }
210 mutex_unlock(&devcgroup_mutex);
211
212 return ret;
213}
214
eb95419b 215static void devcgroup_offline(struct cgroup_subsys_state *css)
1909554c 216{
eb95419b 217 struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
1909554c
AR
218
219 mutex_lock(&devcgroup_mutex);
220 dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
221 mutex_unlock(&devcgroup_mutex);
222}
223
08ce5f16
SH
224/*
225 * called from kernel/cgroup.c with cgroup_lock() held.
226 */
eb95419b
TH
227static struct cgroup_subsys_state *
228devcgroup_css_alloc(struct cgroup_subsys_state *parent_css)
08ce5f16 229{
1909554c 230 struct dev_cgroup *dev_cgroup;
08ce5f16
SH
231
232 dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
233 if (!dev_cgroup)
234 return ERR_PTR(-ENOMEM);
db9aeca9 235 INIT_LIST_HEAD(&dev_cgroup->exceptions);
1909554c 236 dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
08ce5f16 237
08ce5f16
SH
238 return &dev_cgroup->css;
239}
240
eb95419b 241static void devcgroup_css_free(struct cgroup_subsys_state *css)
08ce5f16 242{
eb95419b 243 struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
08ce5f16 244
53eb8c82 245 __dev_exception_clean(dev_cgroup);
08ce5f16
SH
246 kfree(dev_cgroup);
247}
248
249#define DEVCG_ALLOW 1
250#define DEVCG_DENY 2
29486df3
SH
251#define DEVCG_LIST 3
252
17d213f8 253#define MAJMINLEN 13
29486df3 254#define ACCLEN 4
08ce5f16
SH
255
256static void set_access(char *acc, short access)
257{
258 int idx = 0;
29486df3 259 memset(acc, 0, ACCLEN);
08ce5f16
SH
260 if (access & ACC_READ)
261 acc[idx++] = 'r';
262 if (access & ACC_WRITE)
263 acc[idx++] = 'w';
264 if (access & ACC_MKNOD)
265 acc[idx++] = 'm';
266}
267
268static char type_to_char(short type)
269{
270 if (type == DEV_ALL)
271 return 'a';
272 if (type == DEV_CHAR)
273 return 'c';
274 if (type == DEV_BLOCK)
275 return 'b';
276 return 'X';
277}
278
29486df3 279static void set_majmin(char *str, unsigned m)
08ce5f16 280{
08ce5f16 281 if (m == ~0)
7759fc9d 282 strcpy(str, "*");
08ce5f16 283 else
7759fc9d 284 sprintf(str, "%u", m);
08ce5f16
SH
285}
286
182446d0
TH
287static int devcgroup_seq_read(struct cgroup_subsys_state *css,
288 struct cftype *cft, struct seq_file *m)
08ce5f16 289{
182446d0 290 struct dev_cgroup *devcgroup = css_to_devcgroup(css);
db9aeca9 291 struct dev_exception_item *ex;
29486df3 292 char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
08ce5f16 293
4efd1a1b 294 rcu_read_lock();
ad676077
AR
295 /*
296 * To preserve the compatibility:
297 * - Only show the "all devices" when the default policy is to allow
298 * - List the exceptions in case the default policy is to deny
299 * This way, the file remains as a "whitelist of devices"
300 */
5b7aa7d5 301 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
ad676077
AR
302 set_access(acc, ACC_MASK);
303 set_majmin(maj, ~0);
304 set_majmin(min, ~0);
305 seq_printf(m, "%c %s:%s %s\n", type_to_char(DEV_ALL),
29486df3 306 maj, min, acc);
ad676077 307 } else {
db9aeca9
AR
308 list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
309 set_access(acc, ex->access);
310 set_majmin(maj, ex->major);
311 set_majmin(min, ex->minor);
312 seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type),
ad676077
AR
313 maj, min, acc);
314 }
08ce5f16 315 }
4efd1a1b 316 rcu_read_unlock();
08ce5f16 317
29486df3 318 return 0;
08ce5f16
SH
319}
320
ad676077 321/**
db9aeca9
AR
322 * may_access - verifies if a new exception is part of what is allowed
323 * by a dev cgroup based on the default policy +
324 * exceptions. This is used to make sure a child cgroup
325 * won't have more privileges than its parent or to
326 * verify if a certain access is allowed.
ad676077 327 * @dev_cgroup: dev cgroup to be tested against
db9aeca9 328 * @refex: new exception
c39a2a30 329 * @behavior: behavior of the exception
08ce5f16 330 */
26898fdf 331static bool may_access(struct dev_cgroup *dev_cgroup,
c39a2a30
AR
332 struct dev_exception_item *refex,
333 enum devcg_behavior behavior)
08ce5f16 334{
db9aeca9 335 struct dev_exception_item *ex;
ad676077 336 bool match = false;
08ce5f16 337
4b1c7840
TH
338 rcu_lockdep_assert(rcu_read_lock_held() ||
339 lockdep_is_held(&devcgroup_mutex),
340 "device_cgroup::may_access() called without proper synchronization");
341
201e72ac 342 list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) {
db9aeca9 343 if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
08ce5f16 344 continue;
db9aeca9 345 if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR))
08ce5f16 346 continue;
db9aeca9 347 if (ex->major != ~0 && ex->major != refex->major)
08ce5f16 348 continue;
db9aeca9 349 if (ex->minor != ~0 && ex->minor != refex->minor)
08ce5f16 350 continue;
db9aeca9 351 if (refex->access & (~ex->access))
08ce5f16 352 continue;
ad676077
AR
353 match = true;
354 break;
08ce5f16 355 }
ad676077 356
c39a2a30
AR
357 if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
358 if (behavior == DEVCG_DEFAULT_ALLOW) {
359 /* the exception will deny access to certain devices */
360 return true;
361 } else {
362 /* the exception will allow access to certain devices */
363 if (match)
364 /*
365 * a new exception allowing access shouldn't
366 * match an parent's exception
367 */
368 return false;
26898fdf 369 return true;
c39a2a30 370 }
26898fdf 371 } else {
c39a2a30
AR
372 /* only behavior == DEVCG_DEFAULT_DENY allowed here */
373 if (match)
374 /* parent has an exception that matches the proposed */
26898fdf 375 return true;
c39a2a30
AR
376 else
377 return false;
26898fdf
AR
378 }
379 return false;
08ce5f16
SH
380}
381
382/*
383 * parent_has_perm:
db9aeca9 384 * when adding a new allow rule to a device exception list, the rule
08ce5f16
SH
385 * must be allowed in the parent device
386 */
f92523e3 387static int parent_has_perm(struct dev_cgroup *childcg,
db9aeca9 388 struct dev_exception_item *ex)
08ce5f16 389{
63876986 390 struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css));
08ce5f16 391
63876986 392 if (!parent)
08ce5f16 393 return 1;
c39a2a30 394 return may_access(parent, ex, childcg->behavior);
08ce5f16
SH
395}
396
4cef7299
AR
397/**
398 * may_allow_all - checks if it's possible to change the behavior to
399 * allow based on parent's rules.
400 * @parent: device cgroup's parent
401 * returns: != 0 in case it's allowed, 0 otherwise
402 */
403static inline int may_allow_all(struct dev_cgroup *parent)
404{
64e10477
AR
405 if (!parent)
406 return 1;
4cef7299
AR
407 return parent->behavior == DEVCG_DEFAULT_ALLOW;
408}
409
bd2953eb
AR
410/**
411 * revalidate_active_exceptions - walks through the active exception list and
412 * revalidates the exceptions based on parent's
413 * behavior and exceptions. The exceptions that
414 * are no longer valid will be removed.
415 * Called with devcgroup_mutex held.
416 * @devcg: cgroup which exceptions will be checked
417 *
418 * This is one of the three key functions for hierarchy implementation.
419 * This function is responsible for re-evaluating all the cgroup's active
420 * exceptions due to a parent's exception change.
421 * Refer to Documentation/cgroups/devices.txt for more details.
422 */
423static void revalidate_active_exceptions(struct dev_cgroup *devcg)
424{
425 struct dev_exception_item *ex;
426 struct list_head *this, *tmp;
427
428 list_for_each_safe(this, tmp, &devcg->exceptions) {
429 ex = container_of(this, struct dev_exception_item, list);
430 if (!parent_has_perm(devcg, ex))
431 dev_exception_rm(devcg, ex);
432 }
433}
434
bd2953eb
AR
435/**
436 * propagate_exception - propagates a new exception to the children
437 * @devcg_root: device cgroup that added a new exception
438 * @ex: new exception to be propagated
439 *
440 * returns: 0 in case of success, != 0 in case of error
441 */
442static int propagate_exception(struct dev_cgroup *devcg_root,
443 struct dev_exception_item *ex)
444{
492eb21b 445 struct cgroup_subsys_state *pos;
bd2953eb 446 int rc = 0;
bd2953eb 447
d591fb56 448 rcu_read_lock();
bd2953eb 449
492eb21b
TH
450 css_for_each_descendant_pre(pos, &devcg_root->css) {
451 struct dev_cgroup *devcg = css_to_devcgroup(pos);
d591fb56
TH
452
453 /*
454 * Because devcgroup_mutex is held, no devcg will become
455 * online or offline during the tree walk (see on/offline
456 * methods), and online ones are safe to access outside RCU
457 * read lock without bumping refcnt.
458 */
459 if (!is_devcg_online(devcg))
460 continue;
461
462 rcu_read_unlock();
bd2953eb
AR
463
464 /*
465 * in case both root's behavior and devcg is allow, a new
466 * restriction means adding to the exception list
467 */
468 if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW &&
469 devcg->behavior == DEVCG_DEFAULT_ALLOW) {
470 rc = dev_exception_add(devcg, ex);
471 if (rc)
472 break;
473 } else {
474 /*
475 * in the other possible cases:
476 * root's behavior: allow, devcg's: deny
477 * root's behavior: deny, devcg's: deny
478 * the exception will be removed
479 */
480 dev_exception_rm(devcg, ex);
481 }
482 revalidate_active_exceptions(devcg);
483
d591fb56 484 rcu_read_lock();
bd2953eb 485 }
d591fb56
TH
486
487 rcu_read_unlock();
bd2953eb
AR
488 return rc;
489}
490
491static inline bool has_children(struct dev_cgroup *devcgroup)
492{
493 struct cgroup *cgrp = devcgroup->css.cgroup;
494
495 return !list_empty(&cgrp->children);
496}
497
08ce5f16 498/*
db9aeca9 499 * Modify the exception list using allow/deny rules.
08ce5f16
SH
500 * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD
501 * so we can give a container CAP_MKNOD to let it create devices but not
db9aeca9 502 * modify the exception list.
08ce5f16
SH
503 * It seems likely we'll want to add a CAP_CONTAINER capability to allow
504 * us to also grant CAP_SYS_ADMIN to containers without giving away the
db9aeca9 505 * device exception list controls, but for now we'll stick with CAP_SYS_ADMIN
08ce5f16
SH
506 *
507 * Taking rules away is always allowed (given CAP_SYS_ADMIN). Granting
508 * new access is only allowed if you're in the top-level cgroup, or your
509 * parent cgroup has the access you're asking for.
510 */
f92523e3
PM
511static int devcgroup_update_access(struct dev_cgroup *devcgroup,
512 int filetype, const char *buffer)
08ce5f16 513{
f92523e3 514 const char *b;
26fd8405 515 char temp[12]; /* 11 + 1 characters needed for a u32 */
c39a2a30 516 int count, rc = 0;
db9aeca9 517 struct dev_exception_item ex;
63876986 518 struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css));
08ce5f16
SH
519
520 if (!capable(CAP_SYS_ADMIN))
521 return -EPERM;
522
db9aeca9 523 memset(&ex, 0, sizeof(ex));
08ce5f16
SH
524 b = buffer;
525
526 switch (*b) {
527 case 'a':
ad676077
AR
528 switch (filetype) {
529 case DEVCG_ALLOW:
bd2953eb
AR
530 if (has_children(devcgroup))
531 return -EINVAL;
532
4cef7299 533 if (!may_allow_all(parent))
ad676077 534 return -EPERM;
db9aeca9 535 dev_exception_clean(devcgroup);
64e10477
AR
536 devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
537 if (!parent)
538 break;
539
4cef7299
AR
540 rc = dev_exceptions_copy(&devcgroup->exceptions,
541 &parent->exceptions);
542 if (rc)
543 return rc;
ad676077
AR
544 break;
545 case DEVCG_DENY:
bd2953eb
AR
546 if (has_children(devcgroup))
547 return -EINVAL;
548
db9aeca9 549 dev_exception_clean(devcgroup);
5b7aa7d5 550 devcgroup->behavior = DEVCG_DEFAULT_DENY;
ad676077
AR
551 break;
552 default:
553 return -EINVAL;
554 }
555 return 0;
08ce5f16 556 case 'b':
db9aeca9 557 ex.type = DEV_BLOCK;
08ce5f16
SH
558 break;
559 case 'c':
db9aeca9 560 ex.type = DEV_CHAR;
08ce5f16
SH
561 break;
562 default:
f92523e3 563 return -EINVAL;
08ce5f16
SH
564 }
565 b++;
f92523e3
PM
566 if (!isspace(*b))
567 return -EINVAL;
08ce5f16
SH
568 b++;
569 if (*b == '*') {
db9aeca9 570 ex.major = ~0;
08ce5f16
SH
571 b++;
572 } else if (isdigit(*b)) {
26fd8405
AR
573 memset(temp, 0, sizeof(temp));
574 for (count = 0; count < sizeof(temp) - 1; count++) {
575 temp[count] = *b;
576 b++;
577 if (!isdigit(*b))
578 break;
579 }
580 rc = kstrtou32(temp, 10, &ex.major);
581 if (rc)
582 return -EINVAL;
08ce5f16 583 } else {
f92523e3 584 return -EINVAL;
08ce5f16 585 }
f92523e3
PM
586 if (*b != ':')
587 return -EINVAL;
08ce5f16
SH
588 b++;
589
590 /* read minor */
591 if (*b == '*') {
db9aeca9 592 ex.minor = ~0;
08ce5f16
SH
593 b++;
594 } else if (isdigit(*b)) {
26fd8405
AR
595 memset(temp, 0, sizeof(temp));
596 for (count = 0; count < sizeof(temp) - 1; count++) {
597 temp[count] = *b;
598 b++;
599 if (!isdigit(*b))
600 break;
601 }
602 rc = kstrtou32(temp, 10, &ex.minor);
603 if (rc)
604 return -EINVAL;
08ce5f16 605 } else {
f92523e3 606 return -EINVAL;
08ce5f16 607 }
f92523e3
PM
608 if (!isspace(*b))
609 return -EINVAL;
08ce5f16
SH
610 for (b++, count = 0; count < 3; count++, b++) {
611 switch (*b) {
612 case 'r':
db9aeca9 613 ex.access |= ACC_READ;
08ce5f16
SH
614 break;
615 case 'w':
db9aeca9 616 ex.access |= ACC_WRITE;
08ce5f16
SH
617 break;
618 case 'm':
db9aeca9 619 ex.access |= ACC_MKNOD;
08ce5f16
SH
620 break;
621 case '\n':
622 case '\0':
623 count = 3;
624 break;
625 default:
f92523e3 626 return -EINVAL;
08ce5f16
SH
627 }
628 }
629
08ce5f16
SH
630 switch (filetype) {
631 case DEVCG_ALLOW:
db9aeca9 632 if (!parent_has_perm(devcgroup, &ex))
f92523e3 633 return -EPERM;
ad676077
AR
634 /*
635 * If the default policy is to allow by default, try to remove
636 * an matching exception instead. And be silent about it: we
637 * don't want to break compatibility
638 */
5b7aa7d5 639 if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
db9aeca9 640 dev_exception_rm(devcgroup, &ex);
ad676077
AR
641 return 0;
642 }
bd2953eb
AR
643 rc = dev_exception_add(devcgroup, &ex);
644 break;
08ce5f16 645 case DEVCG_DENY:
ad676077
AR
646 /*
647 * If the default policy is to deny by default, try to remove
648 * an matching exception instead. And be silent about it: we
649 * don't want to break compatibility
650 */
bd2953eb 651 if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
db9aeca9 652 dev_exception_rm(devcgroup, &ex);
bd2953eb
AR
653 else
654 rc = dev_exception_add(devcgroup, &ex);
655
656 if (rc)
657 break;
658 /* we only propagate new restrictions */
659 rc = propagate_exception(devcgroup, &ex);
660 break;
08ce5f16 661 default:
bd2953eb 662 rc = -EINVAL;
08ce5f16 663 }
bd2953eb 664 return rc;
f92523e3 665}
08ce5f16 666
182446d0
TH
667static int devcgroup_access_write(struct cgroup_subsys_state *css,
668 struct cftype *cft, const char *buffer)
f92523e3
PM
669{
670 int retval;
b4046f00
LZ
671
672 mutex_lock(&devcgroup_mutex);
182446d0 673 retval = devcgroup_update_access(css_to_devcgroup(css),
f92523e3 674 cft->private, buffer);
b4046f00 675 mutex_unlock(&devcgroup_mutex);
08ce5f16
SH
676 return retval;
677}
678
679static struct cftype dev_cgroup_files[] = {
680 {
681 .name = "allow",
f92523e3 682 .write_string = devcgroup_access_write,
08ce5f16
SH
683 .private = DEVCG_ALLOW,
684 },
685 {
686 .name = "deny",
f92523e3 687 .write_string = devcgroup_access_write,
08ce5f16
SH
688 .private = DEVCG_DENY,
689 },
29486df3
SH
690 {
691 .name = "list",
692 .read_seq_string = devcgroup_seq_read,
693 .private = DEVCG_LIST,
694 },
4baf6e33 695 { } /* terminate */
08ce5f16
SH
696};
697
08ce5f16
SH
698struct cgroup_subsys devices_subsys = {
699 .name = "devices",
700 .can_attach = devcgroup_can_attach,
92fb9748
TH
701 .css_alloc = devcgroup_css_alloc,
702 .css_free = devcgroup_css_free,
1909554c
AR
703 .css_online = devcgroup_online,
704 .css_offline = devcgroup_offline,
08ce5f16 705 .subsys_id = devices_subsys_id,
4baf6e33 706 .base_cftypes = dev_cgroup_files,
08ce5f16
SH
707};
708
ad676077
AR
709/**
710 * __devcgroup_check_permission - checks if an inode operation is permitted
711 * @dev_cgroup: the dev cgroup to be tested against
712 * @type: device type
713 * @major: device major number
714 * @minor: device minor number
715 * @access: combination of ACC_WRITE, ACC_READ and ACC_MKNOD
716 *
717 * returns 0 on success, -EPERM case the operation is not permitted
718 */
8c9506d1 719static int __devcgroup_check_permission(short type, u32 major, u32 minor,
ad676077 720 short access)
08ce5f16 721{
8c9506d1 722 struct dev_cgroup *dev_cgroup;
db9aeca9 723 struct dev_exception_item ex;
ad676077 724 int rc;
36fd71d2 725
db9aeca9
AR
726 memset(&ex, 0, sizeof(ex));
727 ex.type = type;
728 ex.major = major;
729 ex.minor = minor;
730 ex.access = access;
36fd71d2 731
ad676077 732 rcu_read_lock();
8c9506d1 733 dev_cgroup = task_devcgroup(current);
c39a2a30 734 rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
ad676077 735 rcu_read_unlock();
cd500819 736
ad676077
AR
737 if (!rc)
738 return -EPERM;
36fd71d2 739
ad676077
AR
740 return 0;
741}
08ce5f16 742
ad676077
AR
743int __devcgroup_inode_permission(struct inode *inode, int mask)
744{
ad676077
AR
745 short type, access = 0;
746
747 if (S_ISBLK(inode->i_mode))
748 type = DEV_BLOCK;
749 if (S_ISCHR(inode->i_mode))
750 type = DEV_CHAR;
751 if (mask & MAY_WRITE)
752 access |= ACC_WRITE;
753 if (mask & MAY_READ)
754 access |= ACC_READ;
755
8c9506d1
JS
756 return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
757 access);
08ce5f16
SH
758}
759
760int devcgroup_inode_mknod(int mode, dev_t dev)
761{
ad676077 762 short type;
08ce5f16 763
0b82ac37
SH
764 if (!S_ISBLK(mode) && !S_ISCHR(mode))
765 return 0;
766
ad676077
AR
767 if (S_ISBLK(mode))
768 type = DEV_BLOCK;
769 else
770 type = DEV_CHAR;
36fd71d2 771
8c9506d1
JS
772 return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
773 ACC_MKNOD);
36fd71d2 774
08ce5f16 775}