device_cgroup.c 18 KB
Newer Older
1
/*
2
 * device_cgroup.c - device cgroup subsystem
3 4 5 6 7 8 9 10 11
 *
 * Copyright 2007 IBM Corp
 */

#include <linux/device_cgroup.h>
#include <linux/cgroup.h>
#include <linux/ctype.h>
#include <linux/list.h>
#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 18 19 20 21 22 23 24 25

#define ACC_MKNOD 1
#define ACC_READ  2
#define ACC_WRITE 4
#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)

#define DEV_BLOCK 1
#define DEV_CHAR  2
#define DEV_ALL   4  /* this represents all devices */

26 27
static DEFINE_MUTEX(devcgroup_mutex);

28 29 30 31 32 33
enum devcg_behavior {
	DEVCG_DEFAULT_NONE,
	DEVCG_DEFAULT_ALLOW,
	DEVCG_DEFAULT_DENY,
};

34
/*
35
 * exception list locking rules:
36
 * hold devcgroup_mutex for update/read.
37
 * hold rcu_read_lock() for read.
38 39
 */

40
struct dev_exception_item {
41 42 43 44
	u32 major, minor;
	short type;
	short access;
	struct list_head list;
45
	struct rcu_head rcu;
46 47 48 49
};

struct dev_cgroup {
	struct cgroup_subsys_state css;
50
	struct list_head exceptions;
51
	enum devcg_behavior behavior;
52 53
};

54 55
static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
{
56
	return s ? container_of(s, struct dev_cgroup, css) : NULL;
57 58
}

59 60
static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup)
{
61
	return css_to_devcgroup(cgroup_css(cgroup, devices_subsys_id));
62 63
}

64 65
static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
{
66
	return css_to_devcgroup(task_css(task, devices_subsys_id));
67 68
}

69 70
struct cgroup_subsys devices_subsys;

71 72
static int devcgroup_can_attach(struct cgroup *new_cgrp,
				struct cgroup_taskset *set)
73
{
74
	struct task_struct *task = cgroup_taskset_first(set);
75

76 77
	if (current != task && !capable(CAP_SYS_ADMIN))
		return -EPERM;
78 79 80 81
	return 0;
}

/*
82
 * called under devcgroup_mutex
83
 */
84
static int dev_exceptions_copy(struct list_head *dest, struct list_head *orig)
85
{
86
	struct dev_exception_item *ex, *tmp, *new;
87

88 89
	lockdep_assert_held(&devcgroup_mutex);

90 91
	list_for_each_entry(ex, orig, list) {
		new = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
92 93 94 95 96 97 98 99
		if (!new)
			goto free_and_exit;
		list_add_tail(&new->list, dest);
	}

	return 0;

free_and_exit:
100 101 102
	list_for_each_entry_safe(ex, tmp, dest, list) {
		list_del(&ex->list);
		kfree(ex);
103 104 105 106 107
	}
	return -ENOMEM;
}

/*
108
 * called under devcgroup_mutex
109
 */
110 111
static int dev_exception_add(struct dev_cgroup *dev_cgroup,
			     struct dev_exception_item *ex)
112
{
113
	struct dev_exception_item *excopy, *walk;
114

115 116
	lockdep_assert_held(&devcgroup_mutex);

117 118
	excopy = kmemdup(ex, sizeof(*ex), GFP_KERNEL);
	if (!excopy)
119 120
		return -ENOMEM;

121 122
	list_for_each_entry(walk, &dev_cgroup->exceptions, list) {
		if (walk->type != ex->type)
123
			continue;
124
		if (walk->major != ex->major)
125
			continue;
126
		if (walk->minor != ex->minor)
127 128
			continue;

129 130 131
		walk->access |= ex->access;
		kfree(excopy);
		excopy = NULL;
132 133
	}

134 135
	if (excopy != NULL)
		list_add_tail_rcu(&excopy->list, &dev_cgroup->exceptions);
136 137 138 139
	return 0;
}

/*
140
 * called under devcgroup_mutex
141
 */
142 143
static void dev_exception_rm(struct dev_cgroup *dev_cgroup,
			     struct dev_exception_item *ex)
144
{
145
	struct dev_exception_item *walk, *tmp;
146

147 148
	lockdep_assert_held(&devcgroup_mutex);

149 150
	list_for_each_entry_safe(walk, tmp, &dev_cgroup->exceptions, list) {
		if (walk->type != ex->type)
151
			continue;
152
		if (walk->major != ex->major)
153
			continue;
154
		if (walk->minor != ex->minor)
155 156
			continue;

157
		walk->access &= ~ex->access;
158
		if (!walk->access) {
159
			list_del_rcu(&walk->list);
160
			kfree_rcu(walk, rcu);
161 162 163 164
		}
	}
}

165 166 167 168 169 170 171 172 173 174
static void __dev_exception_clean(struct dev_cgroup *dev_cgroup)
{
	struct dev_exception_item *ex, *tmp;

	list_for_each_entry_safe(ex, tmp, &dev_cgroup->exceptions, list) {
		list_del_rcu(&ex->list);
		kfree_rcu(ex, rcu);
	}
}

175
/**
176 177
 * dev_exception_clean - frees all entries of the exception list
 * @dev_cgroup: dev_cgroup with the exception list to be cleaned
178 179 180
 *
 * called under devcgroup_mutex
 */
181
static void dev_exception_clean(struct dev_cgroup *dev_cgroup)
182
{
183 184
	lockdep_assert_held(&devcgroup_mutex);

185
	__dev_exception_clean(dev_cgroup);
186 187
}

188 189 190 191 192
static inline bool is_devcg_online(const struct dev_cgroup *devcg)
{
	return (devcg->behavior != DEVCG_DEFAULT_NONE);
}

193 194 195 196 197 198 199 200
/**
 * devcgroup_online - initializes devcgroup's behavior and exceptions based on
 * 		      parent's
 * @cgroup: cgroup getting online
 * returns 0 in case of success, error code otherwise
 */
static int devcgroup_online(struct cgroup *cgroup)
{
Tejun Heo's avatar
Tejun Heo committed
201 202
	struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup);
	struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(&dev_cgroup->css));
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
	int ret = 0;

	mutex_lock(&devcgroup_mutex);

	if (parent_dev_cgroup == NULL)
		dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW;
	else {
		ret = dev_exceptions_copy(&dev_cgroup->exceptions,
					  &parent_dev_cgroup->exceptions);
		if (!ret)
			dev_cgroup->behavior = parent_dev_cgroup->behavior;
	}
	mutex_unlock(&devcgroup_mutex);

	return ret;
}

static void devcgroup_offline(struct cgroup *cgroup)
{
	struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup);

	mutex_lock(&devcgroup_mutex);
	dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
	mutex_unlock(&devcgroup_mutex);
}

229 230 231
/*
 * called from kernel/cgroup.c with cgroup_lock() held.
 */
232
static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup)
233
{
234
	struct dev_cgroup *dev_cgroup;
235 236 237 238

	dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
	if (!dev_cgroup)
		return ERR_PTR(-ENOMEM);
239
	INIT_LIST_HEAD(&dev_cgroup->exceptions);
240
	dev_cgroup->behavior = DEVCG_DEFAULT_NONE;
241 242 243 244

	return &dev_cgroup->css;
}

245
static void devcgroup_css_free(struct cgroup *cgroup)
246 247 248 249
{
	struct dev_cgroup *dev_cgroup;

	dev_cgroup = cgroup_to_devcgroup(cgroup);
250
	__dev_exception_clean(dev_cgroup);
251 252 253 254 255
	kfree(dev_cgroup);
}

#define DEVCG_ALLOW 1
#define DEVCG_DENY 2
256 257
#define DEVCG_LIST 3

258
#define MAJMINLEN 13
259
#define ACCLEN 4
260 261 262 263

static void set_access(char *acc, short access)
{
	int idx = 0;
264
	memset(acc, 0, ACCLEN);
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
	if (access & ACC_READ)
		acc[idx++] = 'r';
	if (access & ACC_WRITE)
		acc[idx++] = 'w';
	if (access & ACC_MKNOD)
		acc[idx++] = 'm';
}

static char type_to_char(short type)
{
	if (type == DEV_ALL)
		return 'a';
	if (type == DEV_CHAR)
		return 'c';
	if (type == DEV_BLOCK)
		return 'b';
	return 'X';
}

284
static void set_majmin(char *str, unsigned m)
285 286
{
	if (m == ~0)
Li Zefan's avatar
Li Zefan committed
287
		strcpy(str, "*");
288
	else
Li Zefan's avatar
Li Zefan committed
289
		sprintf(str, "%u", m);
290 291
}

292 293
static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
				struct seq_file *m)
294
{
295
	struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup);
296
	struct dev_exception_item *ex;
297
	char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
298

299
	rcu_read_lock();
300 301 302 303 304 305
	/*
	 * To preserve the compatibility:
	 * - Only show the "all devices" when the default policy is to allow
	 * - List the exceptions in case the default policy is to deny
	 * This way, the file remains as a "whitelist of devices"
	 */
306
	if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
307 308 309 310
		set_access(acc, ACC_MASK);
		set_majmin(maj, ~0);
		set_majmin(min, ~0);
		seq_printf(m, "%c %s:%s %s\n", type_to_char(DEV_ALL),
311
			   maj, min, acc);
312
	} else {
313 314 315 316 317
		list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
			set_access(acc, ex->access);
			set_majmin(maj, ex->major);
			set_majmin(min, ex->minor);
			seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type),
318 319
				   maj, min, acc);
		}
320
	}
321
	rcu_read_unlock();
322

323
	return 0;
324 325
}

326
/**
327 328 329 330 331
 * may_access - verifies if a new exception is part of what is allowed
 *		by a dev cgroup based on the default policy +
 *		exceptions. This is used to make sure a child cgroup
 *		won't have more privileges than its parent or to
 *		verify if a certain access is allowed.
332
 * @dev_cgroup: dev cgroup to be tested against
333
 * @refex: new exception
334
 * @behavior: behavior of the exception
335
 */
336
static bool may_access(struct dev_cgroup *dev_cgroup,
337 338
		       struct dev_exception_item *refex,
		       enum devcg_behavior behavior)
339
{
340
	struct dev_exception_item *ex;
341
	bool match = false;
342

343 344 345 346
	rcu_lockdep_assert(rcu_read_lock_held() ||
			   lockdep_is_held(&devcgroup_mutex),
			   "device_cgroup::may_access() called without proper synchronization");

Tejun Heo's avatar
Tejun Heo committed
347
	list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) {
348
		if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
349
			continue;
350
		if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR))
351
			continue;
352
		if (ex->major != ~0 && ex->major != refex->major)
353
			continue;
354
		if (ex->minor != ~0 && ex->minor != refex->minor)
355
			continue;
356
		if (refex->access & (~ex->access))
357
			continue;
358 359
		match = true;
		break;
360
	}
361

362 363 364 365 366 367 368 369 370 371 372 373
	if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
		if (behavior == DEVCG_DEFAULT_ALLOW) {
			/* the exception will deny access to certain devices */
			return true;
		} else {
			/* the exception will allow access to certain devices */
			if (match)
				/*
				 * a new exception allowing access shouldn't
				 * match an parent's exception
				 */
				return false;
374
			return true;
375
		}
376
	} else {
377 378 379
		/* only behavior == DEVCG_DEFAULT_DENY allowed here */
		if (match)
			/* parent has an exception that matches the proposed */
380
			return true;
381 382
		else
			return false;
383 384
	}
	return false;
385 386 387 388
}

/*
 * parent_has_perm:
389
 * when adding a new allow rule to a device exception list, the rule
390 391
 * must be allowed in the parent device
 */
392
static int parent_has_perm(struct dev_cgroup *childcg,
393
				  struct dev_exception_item *ex)
394
{
Tejun Heo's avatar
Tejun Heo committed
395
	struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css));
396

Tejun Heo's avatar
Tejun Heo committed
397
	if (!parent)
398
		return 1;
399
	return may_access(parent, ex, childcg->behavior);
400 401
}

402 403 404 405 406 407 408 409
/**
 * may_allow_all - checks if it's possible to change the behavior to
 *		   allow based on parent's rules.
 * @parent: device cgroup's parent
 * returns: != 0 in case it's allowed, 0 otherwise
 */
static inline int may_allow_all(struct dev_cgroup *parent)
{
410 411
	if (!parent)
		return 1;
412 413 414
	return parent->behavior == DEVCG_DEFAULT_ALLOW;
}

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
/**
 * revalidate_active_exceptions - walks through the active exception list and
 * 				  revalidates the exceptions based on parent's
 * 				  behavior and exceptions. The exceptions that
 * 				  are no longer valid will be removed.
 * 				  Called with devcgroup_mutex held.
 * @devcg: cgroup which exceptions will be checked
 *
 * This is one of the three key functions for hierarchy implementation.
 * This function is responsible for re-evaluating all the cgroup's active
 * exceptions due to a parent's exception change.
 * Refer to Documentation/cgroups/devices.txt for more details.
 */
static void revalidate_active_exceptions(struct dev_cgroup *devcg)
{
	struct dev_exception_item *ex;
	struct list_head *this, *tmp;

	list_for_each_safe(this, tmp, &devcg->exceptions) {
		ex = container_of(this, struct dev_exception_item, list);
		if (!parent_has_perm(devcg, ex))
			dev_exception_rm(devcg, ex);
	}
}

/**
 * propagate_exception - propagates a new exception to the children
 * @devcg_root: device cgroup that added a new exception
 * @ex: new exception to be propagated
 *
 * returns: 0 in case of success, != 0 in case of error
 */
static int propagate_exception(struct dev_cgroup *devcg_root,
			       struct dev_exception_item *ex)
{
450
	struct cgroup *root = devcg_root->css.cgroup, *pos;
451 452
	int rc = 0;

453
	rcu_read_lock();
454

455 456 457 458 459 460 461 462 463 464 465 466 467
	cgroup_for_each_descendant_pre(pos, root) {
		struct dev_cgroup *devcg = cgroup_to_devcgroup(pos);

		/*
		 * Because devcgroup_mutex is held, no devcg will become
		 * online or offline during the tree walk (see on/offline
		 * methods), and online ones are safe to access outside RCU
		 * read lock without bumping refcnt.
		 */
		if (!is_devcg_online(devcg))
			continue;

		rcu_read_unlock();
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488

		/*
		 * in case both root's behavior and devcg is allow, a new
		 * restriction means adding to the exception list
		 */
		if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW &&
		    devcg->behavior == DEVCG_DEFAULT_ALLOW) {
			rc = dev_exception_add(devcg, ex);
			if (rc)
				break;
		} else {
			/*
			 * in the other possible cases:
			 * root's behavior: allow, devcg's: deny
			 * root's behavior: deny, devcg's: deny
			 * the exception will be removed
			 */
			dev_exception_rm(devcg, ex);
		}
		revalidate_active_exceptions(devcg);

489
		rcu_read_lock();
490
	}
491 492

	rcu_read_unlock();
493 494 495 496 497 498 499 500 501 502
	return rc;
}

static inline bool has_children(struct dev_cgroup *devcgroup)
{
	struct cgroup *cgrp = devcgroup->css.cgroup;

	return !list_empty(&cgrp->children);
}

503
/*
504
 * Modify the exception list using allow/deny rules.
505 506
 * CAP_SYS_ADMIN is needed for this.  It's at least separate from CAP_MKNOD
 * so we can give a container CAP_MKNOD to let it create devices but not
507
 * modify the exception list.
508 509
 * It seems likely we'll want to add a CAP_CONTAINER capability to allow
 * us to also grant CAP_SYS_ADMIN to containers without giving away the
510
 * device exception list controls, but for now we'll stick with CAP_SYS_ADMIN
511 512 513 514 515
 *
 * Taking rules away is always allowed (given CAP_SYS_ADMIN).  Granting
 * new access is only allowed if you're in the top-level cgroup, or your
 * parent cgroup has the access you're asking for.
 */
516 517
static int devcgroup_update_access(struct dev_cgroup *devcgroup,
				   int filetype, const char *buffer)
518
{
519
	const char *b;
520
	char temp[12];		/* 11 + 1 characters needed for a u32 */
521
	int count, rc = 0;
522
	struct dev_exception_item ex;
Tejun Heo's avatar
Tejun Heo committed
523
	struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css));
524 525 526 527

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

528
	memset(&ex, 0, sizeof(ex));
529 530 531 532
	b = buffer;

	switch (*b) {
	case 'a':
533 534
		switch (filetype) {
		case DEVCG_ALLOW:
535 536 537
			if (has_children(devcgroup))
				return -EINVAL;

538
			if (!may_allow_all(parent))
539
				return -EPERM;
540
			dev_exception_clean(devcgroup);
541 542 543 544
			devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
			if (!parent)
				break;

545 546 547 548
			rc = dev_exceptions_copy(&devcgroup->exceptions,
						 &parent->exceptions);
			if (rc)
				return rc;
549 550
			break;
		case DEVCG_DENY:
551 552 553
			if (has_children(devcgroup))
				return -EINVAL;

554
			dev_exception_clean(devcgroup);
555
			devcgroup->behavior = DEVCG_DEFAULT_DENY;
556 557 558 559 560
			break;
		default:
			return -EINVAL;
		}
		return 0;
561
	case 'b':
562
		ex.type = DEV_BLOCK;
563 564
		break;
	case 'c':
565
		ex.type = DEV_CHAR;
566 567
		break;
	default:
568
		return -EINVAL;
569 570
	}
	b++;
571 572
	if (!isspace(*b))
		return -EINVAL;
573 574
	b++;
	if (*b == '*') {
575
		ex.major = ~0;
576 577
		b++;
	} else if (isdigit(*b)) {
578 579 580 581 582 583 584 585 586 587
		memset(temp, 0, sizeof(temp));
		for (count = 0; count < sizeof(temp) - 1; count++) {
			temp[count] = *b;
			b++;
			if (!isdigit(*b))
				break;
		}
		rc = kstrtou32(temp, 10, &ex.major);
		if (rc)
			return -EINVAL;
588
	} else {
589
		return -EINVAL;
590
	}
591 592
	if (*b != ':')
		return -EINVAL;
593 594 595 596
	b++;

	/* read minor */
	if (*b == '*') {
597
		ex.minor = ~0;
598 599
		b++;
	} else if (isdigit(*b)) {
600 601 602 603 604 605 606 607 608 609
		memset(temp, 0, sizeof(temp));
		for (count = 0; count < sizeof(temp) - 1; count++) {
			temp[count] = *b;
			b++;
			if (!isdigit(*b))
				break;
		}
		rc = kstrtou32(temp, 10, &ex.minor);
		if (rc)
			return -EINVAL;
610
	} else {
611
		return -EINVAL;
612
	}
613 614
	if (!isspace(*b))
		return -EINVAL;
615 616 617
	for (b++, count = 0; count < 3; count++, b++) {
		switch (*b) {
		case 'r':
618
			ex.access |= ACC_READ;
619 620
			break;
		case 'w':
621
			ex.access |= ACC_WRITE;
622 623
			break;
		case 'm':
624
			ex.access |= ACC_MKNOD;
625 626 627 628 629 630
			break;
		case '\n':
		case '\0':
			count = 3;
			break;
		default:
631
			return -EINVAL;
632 633 634 635 636
		}
	}

	switch (filetype) {
	case DEVCG_ALLOW:
637
		if (!parent_has_perm(devcgroup, &ex))
638
			return -EPERM;
639 640 641 642 643
		/*
		 * If the default policy is to allow by default, try to remove
		 * an matching exception instead. And be silent about it: we
		 * don't want to break compatibility
		 */
644
		if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
645
			dev_exception_rm(devcgroup, &ex);
646 647
			return 0;
		}
648 649
		rc = dev_exception_add(devcgroup, &ex);
		break;
650
	case DEVCG_DENY:
651 652 653 654 655
		/*
		 * If the default policy is to deny by default, try to remove
		 * an matching exception instead. And be silent about it: we
		 * don't want to break compatibility
		 */
656
		if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
657
			dev_exception_rm(devcgroup, &ex);
658 659 660 661 662 663 664 665
		else
			rc = dev_exception_add(devcgroup, &ex);

		if (rc)
			break;
		/* we only propagate new restrictions */
		rc = propagate_exception(devcgroup, &ex);
		break;
666
	default:
667
		rc = -EINVAL;
668
	}
669
	return rc;
670
}
671

672 673 674 675
static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
				  const char *buffer)
{
	int retval;
676 677

	mutex_lock(&devcgroup_mutex);
678 679
	retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp),
					 cft->private, buffer);
680
	mutex_unlock(&devcgroup_mutex);
681 682 683 684 685 686
	return retval;
}

static struct cftype dev_cgroup_files[] = {
	{
		.name = "allow",
687
		.write_string  = devcgroup_access_write,
688 689 690 691
		.private = DEVCG_ALLOW,
	},
	{
		.name = "deny",
692
		.write_string = devcgroup_access_write,
693 694
		.private = DEVCG_DENY,
	},
695 696 697 698 699
	{
		.name = "list",
		.read_seq_string = devcgroup_seq_read,
		.private = DEVCG_LIST,
	},
700
	{ }	/* terminate */
701 702 703 704 705
};

struct cgroup_subsys devices_subsys = {
	.name = "devices",
	.can_attach = devcgroup_can_attach,
706 707
	.css_alloc = devcgroup_css_alloc,
	.css_free = devcgroup_css_free,
708 709
	.css_online = devcgroup_online,
	.css_offline = devcgroup_offline,
710
	.subsys_id = devices_subsys_id,
711
	.base_cftypes = dev_cgroup_files,
712 713
};

714 715 716 717 718 719 720 721 722 723
/**
 * __devcgroup_check_permission - checks if an inode operation is permitted
 * @dev_cgroup: the dev cgroup to be tested against
 * @type: device type
 * @major: device major number
 * @minor: device minor number
 * @access: combination of ACC_WRITE, ACC_READ and ACC_MKNOD
 *
 * returns 0 on success, -EPERM case the operation is not permitted
 */
724
static int __devcgroup_check_permission(short type, u32 major, u32 minor,
725
				        short access)
726
{
727
	struct dev_cgroup *dev_cgroup;
728
	struct dev_exception_item ex;
729
	int rc;
730

731 732 733 734 735
	memset(&ex, 0, sizeof(ex));
	ex.type = type;
	ex.major = major;
	ex.minor = minor;
	ex.access = access;
736

737
	rcu_read_lock();
738
	dev_cgroup = task_devcgroup(current);
739
	rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
740
	rcu_read_unlock();
741

742 743
	if (!rc)
		return -EPERM;
744

745 746
	return 0;
}
747

748 749 750 751 752 753 754 755 756 757 758 759 760
int __devcgroup_inode_permission(struct inode *inode, int mask)
{
	short type, access = 0;

	if (S_ISBLK(inode->i_mode))
		type = DEV_BLOCK;
	if (S_ISCHR(inode->i_mode))
		type = DEV_CHAR;
	if (mask & MAY_WRITE)
		access |= ACC_WRITE;
	if (mask & MAY_READ)
		access |= ACC_READ;

761 762
	return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
			access);
763 764 765 766
}

int devcgroup_inode_mknod(int mode, dev_t dev)
{
767
	short type;
768

769 770 771
	if (!S_ISBLK(mode) && !S_ISCHR(mode))
		return 0;

772 773 774 775
	if (S_ISBLK(mode))
		type = DEV_BLOCK;
	else
		type = DEV_CHAR;
776

777 778
	return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
			ACC_MKNOD);
779

780
}