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