linux/security/device_cgroup.c
<<
>>
Prefs
   1/*
   2 * device_cgroup.c - device cgroup subsystem
   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>
  12#include <linux/seq_file.h>
  13#include <linux/slab.h>
  14#include <linux/rcupdate.h>
  15#include <linux/mutex.h>
  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
  26static DEFINE_MUTEX(devcgroup_mutex);
  27
  28enum devcg_behavior {
  29        DEVCG_DEFAULT_NONE,
  30        DEVCG_DEFAULT_ALLOW,
  31        DEVCG_DEFAULT_DENY,
  32};
  33
  34/*
  35 * exception list locking rules:
  36 * hold devcgroup_mutex for update/read.
  37 * hold rcu_read_lock() for read.
  38 */
  39
  40struct dev_exception_item {
  41        u32 major, minor;
  42        short type;
  43        short access;
  44        struct list_head list;
  45        struct rcu_head rcu;
  46};
  47
  48struct dev_cgroup {
  49        struct cgroup_subsys_state css;
  50        struct list_head exceptions;
  51        enum devcg_behavior behavior;
  52};
  53
  54static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
  55{
  56        return s ? container_of(s, struct dev_cgroup, css) : NULL;
  57}
  58
  59static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
  60{
  61        return css_to_devcgroup(task_css(task, devices_subsys_id));
  62}
  63
  64struct cgroup_subsys devices_subsys;
  65
  66static int devcgroup_can_attach(struct cgroup_subsys_state *new_css,
  67                                struct cgroup_taskset *set)
  68{
  69        struct task_struct *task = cgroup_taskset_first(set);
  70
  71        if (current != task && !capable(CAP_SYS_ADMIN))
  72                return -EPERM;
  73        return 0;
  74}
  75
  76/*
  77 * called under devcgroup_mutex
  78 */
  79static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig)
  80{
  81        struct dev_exception_item *ex, *tmp, *new;
  82
  83        lockdep_assert_held(&devcgroup_mutex);
  84
  85        list_for_each_entry(ex, orig, list) {
  86                new = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
  87                if (!new)
  88                        goto free_and_exit;
  89                list_add_tail(&new->list, dest);
  90        }
  91
  92        return 0;
  93
  94free_and_exit:
  95        list_for_each_entry_safe(ex, tmp, dest, list) {
  96                list_del(&ex->list);
  97                kfree(ex);
  98        }
  99        return -ENOMEM;
 100}
 101
 102/*
 103 * called under devcgroup_mutex
 104 */
 105static int dev_exception_add(struct dev_cgroup *dev_cgroup,
 106                             struct dev_exception_item *ex)
 107{
 108        struct dev_exception_item *excopy, *walk;
 109
 110        lockdep_assert_held(&devcgroup_mutex);
 111
 112        excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
 113        if (!excopy)
 114                return -ENOMEM;
 115
 116        list_for_each_entry(walk, &dev_cgroup->exceptions, list) {
 117                if (walk->type != ex->type)
 118                        continue;
 119                if (walk->major != ex->major)
 120                        continue;
 121                if (walk->minor != ex->minor)
 122                        continue;
 123
 124                walk->access |= ex->access;
 125                kfree(excopy);
 126                excopy = NULL;
 127        }
 128
 129        if (excopy != NULL)
 130                list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions);
 131        return 0;
 132}
 133
 134/*
 135 * called under devcgroup_mutex
 136 */
 137static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
 138                             struct dev_exception_item *ex)
 139{
 140        struct dev_exception_item *walk, *tmp;
 141
 142        lockdep_assert_held(&devcgroup_mutex);
 143
 144        list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) {
 145                if (walk->type != ex->type)
 146                        continue;
 147                if (walk->major != ex->major)
 148                        continue;
 149                if (walk->minor != ex->minor)
 150                        continue;
 151
 152                walk->access &= ~ex->access;
 153                if (!walk->access) {
 154                        list_del_rcu(&walk->list);
 155                        kfree_rcu(walk, rcu);
 156                }
 157        }
 158}
 159
 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
 170/**
 171 * dev_exception_clean - frees all entries of the exception list
 172 * @dev_cgroup: dev_cgroup with the exception list to be cleaned
 173 *
 174 * called under devcgroup_mutex
 175 */
 176static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
 177{
 178        lockdep_assert_held(&devcgroup_mutex);
 179
 180        __dev_exception_clean(dev_cgroup);
 181}
 182
 183static inline bool is_devcg_online(const struct dev_cgroup *devcg)
 184{
 185        return (devcg->behavior != DEVCG_DEFAULT_NONE);
 186}
 187
 188/**
 189 * devcgroup_online - initializes devcgroup's behavior and exceptions based on
 190 *                    parent's
 191 * @css: css getting online
 192 * returns 0 in case of success, error code otherwise
 193 */
 194static int devcgroup_online(struct cgroup_subsys_state *css)
 195{
 196        struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
 197        struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(css));
 198        int ret = 0;
 199
 200        mutex_lock(&devcgroup_mutex);
 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
 215static void devcgroup_offline(struct cgroup_subsys_state *css)
 216{
 217        struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
 218
 219        mutex_lock(&devcgroup_mutex);
 220        dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
 221        mutex_unlock(&devcgroup_mutex);
 222}
 223
 224/*
 225 * called from kernel/cgroup.c with cgroup_lock() held.
 226 */
 227static struct cgroup_subsys_state *
 228devcgroup_css_alloc(struct cgroup_subsys_state *parent_css)
 229{
 230        struct dev_cgroup *dev_cgroup;
 231
 232        dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
 233        if (!dev_cgroup)
 234                return ERR_PTR(-ENOMEM);
 235        INIT_LIST_HEAD(&dev_cgroup->exceptions);
 236        dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
 237
 238        return &dev_cgroup->css;
 239}
 240
 241static void devcgroup_css_free(struct cgroup_subsys_state *css)
 242{
 243        struct dev_cgroup *dev_cgroup = css_to_devcgroup(css);
 244
 245        __dev_exception_clean(dev_cgroup);
 246        kfree(dev_cgroup);
 247}
 248
 249#define DEVCG_ALLOW 1
 250#define DEVCG_DENY 2
 251#define DEVCG_LIST 3
 252
 253#define MAJMINLEN 13
 254#define ACCLEN 4
 255
 256static void set_access(char *acc, short access)
 257{
 258        int idx = 0;
 259        memset(acc, 0, ACCLEN);
 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
 279static void set_majmin(char *str, unsigned m)
 280{
 281        if (m == ~0)
 282                strcpy(str, "*");
 283        else
 284                sprintf(str, "%u", m);
 285}
 286
 287static int devcgroup_seq_read(struct cgroup_subsys_state *css,
 288                              struct cftype *cft, struct seq_file *m)
 289{
 290        struct dev_cgroup *devcgroup = css_to_devcgroup(css);
 291        struct dev_exception_item *ex;
 292        char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
 293
 294        rcu_read_lock();
 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         */
 301        if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
 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),
 306                           maj, min, acc);
 307        } else {
 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),
 313                                   maj, min, acc);
 314                }
 315        }
 316        rcu_read_unlock();
 317
 318        return 0;
 319}
 320
 321/**
 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.
 327 * @dev_cgroup: dev cgroup to be tested against
 328 * @refex: new exception
 329 * @behavior: behavior of the exception
 330 */
 331static bool may_access(struct dev_cgroup *dev_cgroup,
 332                       struct dev_exception_item *refex,
 333                       enum devcg_behavior behavior)
 334{
 335        struct dev_exception_item *ex;
 336        bool match = false;
 337
 338        rcu_lockdep_assert(rcu_read_lock_held() ||
 339                           lockdep_is_held(&devcgroup_mutex),
 340                           "device_cgroup::may_access() called without proper synchronization");
 341
 342        list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) {
 343                if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
 344                        continue;
 345                if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR))
 346                        continue;
 347                if (ex->major != ~0 && ex->major != refex->major)
 348                        continue;
 349                if (ex->minor != ~0 && ex->minor != refex->minor)
 350                        continue;
 351                if (refex->access & (~ex->access))
 352                        continue;
 353                match = true;
 354                break;
 355        }
 356
 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;
 369                        return true;
 370                }
 371        } else {
 372                /* only behavior == DEVCG_DEFAULT_DENY allowed here */
 373                if (match)
 374                        /* parent has an exception that matches the proposed */
 375                        return true;
 376                else
 377                        return false;
 378        }
 379        return false;
 380}
 381
 382/*
 383 * parent_has_perm:
 384 * when adding a new allow rule to a device exception list, the rule
 385 * must be allowed in the parent device
 386 */
 387static int parent_has_perm(struct dev_cgroup *childcg,
 388                                  struct dev_exception_item *ex)
 389{
 390        struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css));
 391
 392        if (!parent)
 393                return 1;
 394        return may_access(parent, ex, childcg->behavior);
 395}
 396
 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{
 405        if (!parent)
 406                return 1;
 407        return parent->behavior == DEVCG_DEFAULT_ALLOW;
 408}
 409
 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
 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{
 445        struct cgroup_subsys_state *pos;
 446        int rc = 0;
 447
 448        rcu_read_lock();
 449
 450        css_for_each_descendant_pre(pos, &devcg_root->css) {
 451                struct dev_cgroup *devcg = css_to_devcgroup(pos);
 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 (pos == &devcg_root->css || !is_devcg_online(devcg))
 460                        continue;
 461
 462                rcu_read_unlock();
 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
 484                rcu_read_lock();
 485        }
 486
 487        rcu_read_unlock();
 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
 498/*
 499 * Modify the exception list using allow/deny rules.
 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
 502 * modify the exception list.
 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
 505 * device exception list controls, but for now we'll stick with CAP_SYS_ADMIN
 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 */
 511static int devcgroup_update_access(struct dev_cgroup *devcgroup,
 512                                   int filetype, const char *buffer)
 513{
 514        const char *b;
 515        char temp[12];          /* 11 + 1 characters needed for a u32 */
 516        int count, rc = 0;
 517        struct dev_exception_item ex;
 518        struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css));
 519
 520        if (!capable(CAP_SYS_ADMIN))
 521                return -EPERM;
 522
 523        memset(&ex, 0, sizeof(ex));
 524        b = buffer;
 525
 526        switch (*b) {
 527        case 'a':
 528                switch (filetype) {
 529                case DEVCG_ALLOW:
 530                        if (has_children(devcgroup))
 531                                return -EINVAL;
 532
 533                        if (!may_allow_all(parent))
 534                                return -EPERM;
 535                        dev_exception_clean(devcgroup);
 536                        devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
 537                        if (!parent)
 538                                break;
 539
 540                        rc = dev_exceptions_copy(&devcgroup->exceptions,
 541                                                 &parent->exceptions);
 542                        if (rc)
 543                                return rc;
 544                        break;
 545                case DEVCG_DENY:
 546                        if (has_children(devcgroup))
 547                                return -EINVAL;
 548
 549                        dev_exception_clean(devcgroup);
 550                        devcgroup->behavior = DEVCG_DEFAULT_DENY;
 551                        break;
 552                default:
 553                        return -EINVAL;
 554                }
 555                return 0;
 556        case 'b':
 557                ex.type = DEV_BLOCK;
 558                break;
 559        case 'c':
 560                ex.type = DEV_CHAR;
 561                break;
 562        default:
 563                return -EINVAL;
 564        }
 565        b++;
 566        if (!isspace(*b))
 567                return -EINVAL;
 568        b++;
 569        if (*b == '*') {
 570                ex.major = ~0;
 571                b++;
 572        } else if (isdigit(*b)) {
 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;
 583        } else {
 584                return -EINVAL;
 585        }
 586        if (*b != ':')
 587                return -EINVAL;
 588        b++;
 589
 590        /* read minor */
 591        if (*b == '*') {
 592                ex.minor = ~0;
 593                b++;
 594        } else if (isdigit(*b)) {
 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;
 605        } else {
 606                return -EINVAL;
 607        }
 608        if (!isspace(*b))
 609                return -EINVAL;
 610        for (b++, count = 0; count < 3; count++, b++) {
 611                switch (*b) {
 612                case 'r':
 613                        ex.access |= ACC_READ;
 614                        break;
 615                case 'w':
 616                        ex.access |= ACC_WRITE;
 617                        break;
 618                case 'm':
 619                        ex.access |= ACC_MKNOD;
 620                        break;
 621                case '\n':
 622                case '\0':
 623                        count = 3;
 624                        break;
 625                default:
 626                        return -EINVAL;
 627                }
 628        }
 629
 630        switch (filetype) {
 631        case DEVCG_ALLOW:
 632                if (!parent_has_perm(devcgroup, &ex))
 633                        return -EPERM;
 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                 */
 639                if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
 640                        dev_exception_rm(devcgroup, &ex);
 641                        return 0;
 642                }
 643                rc = dev_exception_add(devcgroup, &ex);
 644                break;
 645        case DEVCG_DENY:
 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                 */
 651                if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
 652                        dev_exception_rm(devcgroup, &ex);
 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;
 661        default:
 662                rc = -EINVAL;
 663        }
 664        return rc;
 665}
 666
 667static int devcgroup_access_write(struct cgroup_subsys_state *css,
 668                                  struct cftype *cft, const char *buffer)
 669{
 670        int retval;
 671
 672        mutex_lock(&devcgroup_mutex);
 673        retval = devcgroup_update_access(css_to_devcgroup(css),
 674                                         cft->private, buffer);
 675        mutex_unlock(&devcgroup_mutex);
 676        return retval;
 677}
 678
 679static struct cftype dev_cgroup_files[] = {
 680        {
 681                .name = "allow",
 682                .write_string  = devcgroup_access_write,
 683                .private = DEVCG_ALLOW,
 684        },
 685        {
 686                .name = "deny",
 687                .write_string = devcgroup_access_write,
 688                .private = DEVCG_DENY,
 689        },
 690        {
 691                .name = "list",
 692                .read_seq_string = devcgroup_seq_read,
 693                .private = DEVCG_LIST,
 694        },
 695        { }     /* terminate */
 696};
 697
 698struct cgroup_subsys devices_subsys = {
 699        .name = "devices",
 700        .can_attach = devcgroup_can_attach,
 701        .css_alloc = devcgroup_css_alloc,
 702        .css_free = devcgroup_css_free,
 703        .css_online = devcgroup_online,
 704        .css_offline = devcgroup_offline,
 705        .subsys_id = devices_subsys_id,
 706        .base_cftypes = dev_cgroup_files,
 707};
 708
 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 */
 719static int __devcgroup_check_permission(short type, u32 major, u32 minor,
 720                                        short access)
 721{
 722        struct dev_cgroup *dev_cgroup;
 723        struct dev_exception_item ex;
 724        int rc;
 725
 726        memset(&ex, 0, sizeof(ex));
 727        ex.type = type;
 728        ex.major = major;
 729        ex.minor = minor;
 730        ex.access = access;
 731
 732        rcu_read_lock();
 733        dev_cgroup = task_devcgroup(current);
 734        rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
 735        rcu_read_unlock();
 736
 737        if (!rc)
 738                return -EPERM;
 739
 740        return 0;
 741}
 742
 743int __devcgroup_inode_permission(struct inode *inode, int mask)
 744{
 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
 756        return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
 757                        access);
 758}
 759
 760int devcgroup_inode_mknod(int mode, dev_t dev)
 761{
 762        short type;
 763
 764        if (!S_ISBLK(mode) && !S_ISCHR(mode))
 765                return 0;
 766
 767        if (S_ISBLK(mode))
 768                type = DEV_BLOCK;
 769        else
 770                type = DEV_CHAR;
 771
 772        return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
 773                        ACC_MKNOD);
 774
 775}
 776