device_cgroup.c 18.1 KB
Newer Older
1
/*
L
Lai Jiangshan 已提交
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>
L
Lai Jiangshan 已提交
14
#include <linux/rcupdate.h>
L
Li Zefan 已提交
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 */

L
Li Zefan 已提交
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:
L
Li Zefan 已提交
36
 * hold devcgroup_mutex for update/read.
L
Lai Jiangshan 已提交
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;
}

/*
L
Li Zefan 已提交
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

T
Tejun Heo 已提交
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;
}

/*
L
Li Zefan 已提交
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

T
Tejun Heo 已提交
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;
}

/*
L
Li Zefan 已提交
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

T
Tejun Heo 已提交
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
{
T
Tejun Heo 已提交
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 201 202 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 229 230
/**
 * 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)
{
	struct dev_cgroup *dev_cgroup, *parent_dev_cgroup = NULL;
	int ret = 0;

	mutex_lock(&devcgroup_mutex);
	dev_cgroup = cgroup_to_devcgroup(cgroup);
	if (cgroup->parent)
		parent_dev_cgroup = cgroup_to_devcgroup(cgroup->parent);

	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);
}

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

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

	return &dev_cgroup->css;
}

247
static void devcgroup_css_free(struct cgroup *cgroup)
248 249 250 251
{
	struct dev_cgroup *dev_cgroup;

	dev_cgroup = cgroup_to_devcgroup(cgroup);
252
	__dev_exception_clean(dev_cgroup);
253 254 255 256 257
	kfree(dev_cgroup);
}

#define DEVCG_ALLOW 1
#define DEVCG_DENY 2
258 259
#define DEVCG_LIST 3

260
#define MAJMINLEN 13
261
#define ACCLEN 4
262 263 264 265

static void set_access(char *acc, short access)
{
	int idx = 0;
266
	memset(acc, 0, ACCLEN);
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	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';
}

286
static void set_majmin(char *str, unsigned m)
287 288
{
	if (m == ~0)
L
Li Zefan 已提交
289
		strcpy(str, "*");
290
	else
L
Li Zefan 已提交
291
		sprintf(str, "%u", m);
292 293
}

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

301
	rcu_read_lock();
302 303 304 305 306 307
	/*
	 * 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"
	 */
308
	if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
309 310 311 312
		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),
313
			   maj, min, acc);
314
	} else {
315 316 317 318 319
		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),
320 321
				   maj, min, acc);
		}
322
	}
323
	rcu_read_unlock();
324

325
	return 0;
326 327
}

328
/**
329 330 331 332 333
 * 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.
334
 * @dev_cgroup: dev cgroup to be tested against
335
 * @refex: new exception
336
 * @behavior: behavior of the exception
337
 */
338
static bool may_access(struct dev_cgroup *dev_cgroup,
339 340
		       struct dev_exception_item *refex,
		       enum devcg_behavior behavior)
341
{
342
	struct dev_exception_item *ex;
343
	bool match = false;
344

T
Tejun Heo 已提交
345 346 347 348
	rcu_lockdep_assert(rcu_read_lock_held() ||
			   lockdep_is_held(&devcgroup_mutex),
			   "device_cgroup::may_access() called without proper synchronization");

T
Tejun Heo 已提交
349
	list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) {
350
		if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
351
			continue;
352
		if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR))
353
			continue;
354
		if (ex->major != ~0 && ex->major != refex->major)
355
			continue;
356
		if (ex->minor != ~0 && ex->minor != refex->minor)
357
			continue;
358
		if (refex->access & (~ex->access))
359
			continue;
360 361
		match = true;
		break;
362
	}
363

364 365 366 367 368 369 370 371 372 373 374 375
	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;
376
			return true;
377
		}
378
	} else {
379 380 381
		/* only behavior == DEVCG_DEFAULT_DENY allowed here */
		if (match)
			/* parent has an exception that matches the proposed */
382
			return true;
383 384
		else
			return false;
385 386
	}
	return false;
387 388 389 390
}

/*
 * parent_has_perm:
391
 * when adding a new allow rule to a device exception list, the rule
392 393
 * must be allowed in the parent device
 */
394
static int parent_has_perm(struct dev_cgroup *childcg,
395
				  struct dev_exception_item *ex)
396
{
397
	struct cgroup *pcg = childcg->css.cgroup->parent;
398 399 400 401 402
	struct dev_cgroup *parent;

	if (!pcg)
		return 1;
	parent = cgroup_to_devcgroup(pcg);
403
	return may_access(parent, ex, childcg->behavior);
404 405
}

406 407 408 409 410 411 412 413
/**
 * 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)
{
414 415
	if (!parent)
		return 1;
416 417 418
	return parent->behavior == DEVCG_DEFAULT_ALLOW;
}

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 450 451 452 453
/**
 * 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)
{
454
	struct cgroup *root = devcg_root->css.cgroup, *pos;
455 456
	int rc = 0;

457
	rcu_read_lock();
458

459 460 461 462 463 464 465 466 467 468 469 470 471
	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();
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492

		/*
		 * 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);

493
		rcu_read_lock();
494
	}
495 496

	rcu_read_unlock();
497 498 499 500 501 502 503 504 505 506
	return rc;
}

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

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

507
/*
508
 * Modify the exception list using allow/deny rules.
509 510
 * 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
511
 * modify the exception list.
512 513
 * 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
514
 * device exception list controls, but for now we'll stick with CAP_SYS_ADMIN
515 516 517 518 519
 *
 * 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.
 */
520 521
static int devcgroup_update_access(struct dev_cgroup *devcgroup,
				   int filetype, const char *buffer)
522
{
523
	const char *b;
524
	char temp[12];		/* 11 + 1 characters needed for a u32 */
525
	int count, rc = 0;
526
	struct dev_exception_item ex;
527
	struct cgroup *p = devcgroup->css.cgroup;
528
	struct dev_cgroup *parent = NULL;
529 530 531 532

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

533 534 535
	if (p->parent)
		parent = cgroup_to_devcgroup(p->parent);

536
	memset(&ex, 0, sizeof(ex));
537 538 539 540
	b = buffer;

	switch (*b) {
	case 'a':
541 542
		switch (filetype) {
		case DEVCG_ALLOW:
543 544 545
			if (has_children(devcgroup))
				return -EINVAL;

546
			if (!may_allow_all(parent))
547
				return -EPERM;
548
			dev_exception_clean(devcgroup);
549 550 551 552
			devcgroup->behavior = DEVCG_DEFAULT_ALLOW;
			if (!parent)
				break;

553 554 555 556
			rc = dev_exceptions_copy(&devcgroup->exceptions,
						 &parent->exceptions);
			if (rc)
				return rc;
557 558
			break;
		case DEVCG_DENY:
559 560 561
			if (has_children(devcgroup))
				return -EINVAL;

562
			dev_exception_clean(devcgroup);
563
			devcgroup->behavior = DEVCG_DEFAULT_DENY;
564 565 566 567 568
			break;
		default:
			return -EINVAL;
		}
		return 0;
569
	case 'b':
570
		ex.type = DEV_BLOCK;
571 572
		break;
	case 'c':
573
		ex.type = DEV_CHAR;
574 575
		break;
	default:
576
		return -EINVAL;
577 578
	}
	b++;
579 580
	if (!isspace(*b))
		return -EINVAL;
581 582
	b++;
	if (*b == '*') {
583
		ex.major = ~0;
584 585
		b++;
	} else if (isdigit(*b)) {
586 587 588 589 590 591 592 593 594 595
		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;
596
	} else {
597
		return -EINVAL;
598
	}
599 600
	if (*b != ':')
		return -EINVAL;
601 602 603 604
	b++;

	/* read minor */
	if (*b == '*') {
605
		ex.minor = ~0;
606 607
		b++;
	} else if (isdigit(*b)) {
608 609 610 611 612 613 614 615 616 617
		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;
618
	} else {
619
		return -EINVAL;
620
	}
621 622
	if (!isspace(*b))
		return -EINVAL;
623 624 625
	for (b++, count = 0; count < 3; count++, b++) {
		switch (*b) {
		case 'r':
626
			ex.access |= ACC_READ;
627 628
			break;
		case 'w':
629
			ex.access |= ACC_WRITE;
630 631
			break;
		case 'm':
632
			ex.access |= ACC_MKNOD;
633 634 635 636 637 638
			break;
		case '\n':
		case '\0':
			count = 3;
			break;
		default:
639
			return -EINVAL;
640 641 642 643 644
		}
	}

	switch (filetype) {
	case DEVCG_ALLOW:
645
		if (!parent_has_perm(devcgroup, &ex))
646
			return -EPERM;
647 648 649 650 651
		/*
		 * 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
		 */
652
		if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
653
			dev_exception_rm(devcgroup, &ex);
654 655
			return 0;
		}
656 657
		rc = dev_exception_add(devcgroup, &ex);
		break;
658
	case DEVCG_DENY:
659 660 661 662 663
		/*
		 * 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
		 */
664
		if (devcgroup->behavior == DEVCG_DEFAULT_DENY)
665
			dev_exception_rm(devcgroup, &ex);
666 667 668 669 670 671 672 673
		else
			rc = dev_exception_add(devcgroup, &ex);

		if (rc)
			break;
		/* we only propagate new restrictions */
		rc = propagate_exception(devcgroup, &ex);
		break;
674
	default:
675
		rc = -EINVAL;
676
	}
677
	return rc;
678
}
679

680 681 682 683
static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
				  const char *buffer)
{
	int retval;
L
Li Zefan 已提交
684 685

	mutex_lock(&devcgroup_mutex);
686 687
	retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp),
					 cft->private, buffer);
L
Li Zefan 已提交
688
	mutex_unlock(&devcgroup_mutex);
689 690 691 692 693 694
	return retval;
}

static struct cftype dev_cgroup_files[] = {
	{
		.name = "allow",
695
		.write_string  = devcgroup_access_write,
696 697 698 699
		.private = DEVCG_ALLOW,
	},
	{
		.name = "deny",
700
		.write_string = devcgroup_access_write,
701 702
		.private = DEVCG_DENY,
	},
703 704 705 706 707
	{
		.name = "list",
		.read_seq_string = devcgroup_seq_read,
		.private = DEVCG_LIST,
	},
708
	{ }	/* terminate */
709 710 711 712 713
};

struct cgroup_subsys devices_subsys = {
	.name = "devices",
	.can_attach = devcgroup_can_attach,
714 715
	.css_alloc = devcgroup_css_alloc,
	.css_free = devcgroup_css_free,
716 717
	.css_online = devcgroup_online,
	.css_offline = devcgroup_offline,
718
	.subsys_id = devices_subsys_id,
719
	.base_cftypes = dev_cgroup_files,
720 721
};

722 723 724 725 726 727 728 729 730 731
/**
 * __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
 */
J
Jiri Slaby 已提交
732
static int __devcgroup_check_permission(short type, u32 major, u32 minor,
733
				        short access)
734
{
J
Jiri Slaby 已提交
735
	struct dev_cgroup *dev_cgroup;
736
	struct dev_exception_item ex;
737
	int rc;
L
Li Zefan 已提交
738

739 740 741 742 743
	memset(&ex, 0, sizeof(ex));
	ex.type = type;
	ex.major = major;
	ex.minor = minor;
	ex.access = access;
L
Li Zefan 已提交
744

745
	rcu_read_lock();
J
Jiri Slaby 已提交
746
	dev_cgroup = task_devcgroup(current);
747
	rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
748
	rcu_read_unlock();
749

750 751
	if (!rc)
		return -EPERM;
L
Li Zefan 已提交
752

753 754
	return 0;
}
755

756 757 758 759 760 761 762 763 764 765 766 767 768
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;

J
Jiri Slaby 已提交
769 770
	return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
			access);
771 772 773 774
}

int devcgroup_inode_mknod(int mode, dev_t dev)
{
775
	short type;
776

S
Serge E. Hallyn 已提交
777 778 779
	if (!S_ISBLK(mode) && !S_ISCHR(mode))
		return 0;

780 781 782 783
	if (S_ISBLK(mode))
		type = DEV_BLOCK;
	else
		type = DEV_CHAR;
L
Li Zefan 已提交
784

J
Jiri Slaby 已提交
785 786
	return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
			ACC_MKNOD);
L
Li Zefan 已提交
787

788
}