2016-12-27 22:49:09 +03:00
# include "cgroup-internal.h"
2017-02-06 12:57:33 +03:00
# include <linux/sched/task.h>
2016-12-27 22:49:09 +03:00
# include <linux/slab.h>
# include <linux/nsproxy.h>
# include <linux/proc_ns.h>
/* cgroup namespaces */
static struct ucounts * inc_cgroup_namespaces ( struct user_namespace * ns )
{
return inc_ucount ( ns , current_euid ( ) , UCOUNT_CGROUP_NAMESPACES ) ;
}
static void dec_cgroup_namespaces ( struct ucounts * ucounts )
{
dec_ucount ( ucounts , UCOUNT_CGROUP_NAMESPACES ) ;
}
static struct cgroup_namespace * alloc_cgroup_ns ( void )
{
struct cgroup_namespace * new_ns ;
int ret ;
new_ns = kzalloc ( sizeof ( struct cgroup_namespace ) , GFP_KERNEL ) ;
if ( ! new_ns )
return ERR_PTR ( - ENOMEM ) ;
ret = ns_alloc_inum ( & new_ns - > ns ) ;
if ( ret ) {
kfree ( new_ns ) ;
return ERR_PTR ( ret ) ;
}
atomic_set ( & new_ns - > count , 1 ) ;
new_ns - > ns . ops = & cgroupns_operations ;
return new_ns ;
}
void free_cgroup_ns ( struct cgroup_namespace * ns )
{
put_css_set ( ns - > root_cset ) ;
dec_cgroup_namespaces ( ns - > ucounts ) ;
put_user_ns ( ns - > user_ns ) ;
ns_free_inum ( & ns - > ns ) ;
kfree ( ns ) ;
}
EXPORT_SYMBOL ( free_cgroup_ns ) ;
struct cgroup_namespace * copy_cgroup_ns ( unsigned long flags ,
struct user_namespace * user_ns ,
struct cgroup_namespace * old_ns )
{
struct cgroup_namespace * new_ns ;
struct ucounts * ucounts ;
struct css_set * cset ;
BUG_ON ( ! old_ns ) ;
if ( ! ( flags & CLONE_NEWCGROUP ) ) {
get_cgroup_ns ( old_ns ) ;
return old_ns ;
}
/* Allow only sysadmin to create cgroup namespace. */
if ( ! ns_capable ( user_ns , CAP_SYS_ADMIN ) )
return ERR_PTR ( - EPERM ) ;
ucounts = inc_cgroup_namespaces ( user_ns ) ;
if ( ! ucounts )
return ERR_PTR ( - ENOSPC ) ;
/* It is not safe to take cgroup_mutex here */
spin_lock_irq ( & css_set_lock ) ;
cset = task_css_set ( current ) ;
get_css_set ( cset ) ;
spin_unlock_irq ( & css_set_lock ) ;
new_ns = alloc_cgroup_ns ( ) ;
if ( IS_ERR ( new_ns ) ) {
put_css_set ( cset ) ;
dec_cgroup_namespaces ( ucounts ) ;
return new_ns ;
}
new_ns - > user_ns = get_user_ns ( user_ns ) ;
new_ns - > ucounts = ucounts ;
new_ns - > root_cset = cset ;
return new_ns ;
}
static inline struct cgroup_namespace * to_cg_ns ( struct ns_common * ns )
{
return container_of ( ns , struct cgroup_namespace , ns ) ;
}
static int cgroupns_install ( struct nsproxy * nsproxy , struct ns_common * ns )
{
struct cgroup_namespace * cgroup_ns = to_cg_ns ( ns ) ;
if ( ! ns_capable ( current_user_ns ( ) , CAP_SYS_ADMIN ) | |
! ns_capable ( cgroup_ns - > user_ns , CAP_SYS_ADMIN ) )
return - EPERM ;
/* Don't need to do anything if we are attaching to our own cgroupns. */
if ( cgroup_ns = = nsproxy - > cgroup_ns )
return 0 ;
get_cgroup_ns ( cgroup_ns ) ;
put_cgroup_ns ( nsproxy - > cgroup_ns ) ;
nsproxy - > cgroup_ns = cgroup_ns ;
return 0 ;
}
static struct ns_common * cgroupns_get ( struct task_struct * task )
{
struct cgroup_namespace * ns = NULL ;
struct nsproxy * nsproxy ;
task_lock ( task ) ;
nsproxy = task - > nsproxy ;
if ( nsproxy ) {
ns = nsproxy - > cgroup_ns ;
get_cgroup_ns ( ns ) ;
}
task_unlock ( task ) ;
return ns ? & ns - > ns : NULL ;
}
static void cgroupns_put ( struct ns_common * ns )
{
put_cgroup_ns ( to_cg_ns ( ns ) ) ;
}
static struct user_namespace * cgroupns_owner ( struct ns_common * ns )
{
return to_cg_ns ( ns ) - > user_ns ;
}
const struct proc_ns_operations cgroupns_operations = {
. name = " cgroup " ,
. type = CLONE_NEWCGROUP ,
. get = cgroupns_get ,
. put = cgroupns_put ,
. install = cgroupns_install ,
. owner = cgroupns_owner ,
} ;
static __init int cgroup_namespaces_init ( void )
{
return 0 ;
}
subsys_initcall ( cgroup_namespaces_init ) ;