2006-10-02 13:18:06 +04:00
/*
* Copyright ( C ) 2006 IBM Corporation
*
* Author : Serge Hallyn < serue @ us . ibm . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation , version 2 of the
* License .
2006-10-02 13:18:19 +04:00
*
* Jun 2006 - namespaces support
* OpenVZ , SWsoft Inc .
* Pavel Emelianov < xemul @ openvz . org >
2006-10-02 13:18:06 +04:00
*/
# include <linux/module.h>
# include <linux/nsproxy.h>
2006-10-02 13:18:07 +04:00
# include <linux/init_task.h>
2006-12-08 13:37:56 +03:00
# include <linux/mnt_namespace.h>
2006-10-02 13:18:14 +04:00
# include <linux/utsname.h>
2006-12-08 13:37:59 +03:00
# include <linux/pid_namespace.h>
2007-09-27 09:04:26 +04:00
# include <net/net_namespace.h>
2008-02-08 15:18:22 +03:00
# include <linux/ipc_namespace.h>
2006-10-02 13:18:07 +04:00
2007-07-16 10:41:07 +04:00
static struct kmem_cache * nsproxy_cachep ;
2006-10-02 13:18:07 +04:00
struct nsproxy init_nsproxy = INIT_NSPROXY ( init_nsproxy ) ;
2006-10-02 13:18:06 +04:00
/*
* creates a copy of " orig " with refcount 1.
*/
2007-05-08 11:25:21 +04:00
static inline struct nsproxy * clone_nsproxy ( struct nsproxy * orig )
2006-10-02 13:18:06 +04:00
{
struct nsproxy * ns ;
2007-07-16 10:41:07 +04:00
ns = kmem_cache_alloc ( nsproxy_cachep , GFP_KERNEL ) ;
if ( ns ) {
memcpy ( ns , orig , sizeof ( struct nsproxy ) ) ;
2006-10-02 13:18:06 +04:00
atomic_set ( & ns - > count , 1 ) ;
2007-07-16 10:41:07 +04:00
}
2006-10-02 13:18:06 +04:00
return ns ;
}
/*
2007-05-08 11:25:21 +04:00
* Create new nsproxy and all of its the associated namespaces .
* Return the newly created nsproxy . Do not attach this to the task ,
* leave it to the caller to do proper locking and attach it to task .
2006-10-02 13:18:06 +04:00
*/
2007-07-16 10:41:15 +04:00
static struct nsproxy * create_new_namespaces ( unsigned long flags ,
struct task_struct * tsk , struct fs_struct * new_fs )
2006-10-02 13:18:06 +04:00
{
2007-05-08 11:25:21 +04:00
struct nsproxy * new_nsp ;
2007-07-16 10:41:06 +04:00
int err ;
2006-10-02 13:18:06 +04:00
2007-05-08 11:25:21 +04:00
new_nsp = clone_nsproxy ( tsk - > nsproxy ) ;
if ( ! new_nsp )
return ERR_PTR ( - ENOMEM ) ;
2006-10-02 13:18:08 +04:00
2007-05-08 11:25:21 +04:00
new_nsp - > mnt_ns = copy_mnt_ns ( flags , tsk - > nsproxy - > mnt_ns , new_fs ) ;
2007-07-16 10:41:06 +04:00
if ( IS_ERR ( new_nsp - > mnt_ns ) ) {
err = PTR_ERR ( new_nsp - > mnt_ns ) ;
2007-05-08 11:25:21 +04:00
goto out_ns ;
2007-07-16 10:41:06 +04:00
}
2007-05-08 11:25:21 +04:00
new_nsp - > uts_ns = copy_utsname ( flags , tsk - > nsproxy - > uts_ns ) ;
2007-07-16 10:41:06 +04:00
if ( IS_ERR ( new_nsp - > uts_ns ) ) {
err = PTR_ERR ( new_nsp - > uts_ns ) ;
2007-05-08 11:25:21 +04:00
goto out_uts ;
2007-07-16 10:41:06 +04:00
}
2007-05-08 11:25:21 +04:00
new_nsp - > ipc_ns = copy_ipcs ( flags , tsk - > nsproxy - > ipc_ns ) ;
2007-07-16 10:41:06 +04:00
if ( IS_ERR ( new_nsp - > ipc_ns ) ) {
err = PTR_ERR ( new_nsp - > ipc_ns ) ;
2007-05-08 11:25:21 +04:00
goto out_ipc ;
2007-07-16 10:41:06 +04:00
}
2007-05-08 11:25:21 +04:00
2007-10-19 10:39:49 +04:00
new_nsp - > pid_ns = copy_pid_ns ( flags , task_active_pid_ns ( tsk ) ) ;
2007-07-16 10:41:06 +04:00
if ( IS_ERR ( new_nsp - > pid_ns ) ) {
err = PTR_ERR ( new_nsp - > pid_ns ) ;
2007-05-08 11:25:21 +04:00
goto out_pid ;
2007-07-16 10:41:06 +04:00
}
2007-05-08 11:25:21 +04:00
2007-09-27 09:04:26 +04:00
new_nsp - > net_ns = copy_net_ns ( flags , tsk - > nsproxy - > net_ns ) ;
if ( IS_ERR ( new_nsp - > net_ns ) ) {
err = PTR_ERR ( new_nsp - > net_ns ) ;
goto out_net ;
}
2007-05-08 11:25:21 +04:00
return new_nsp ;
2007-09-27 09:04:26 +04:00
out_net :
2007-07-16 10:40:59 +04:00
if ( new_nsp - > pid_ns )
put_pid_ns ( new_nsp - > pid_ns ) ;
2007-05-08 11:25:21 +04:00
out_pid :
if ( new_nsp - > ipc_ns )
put_ipc_ns ( new_nsp - > ipc_ns ) ;
out_ipc :
if ( new_nsp - > uts_ns )
put_uts_ns ( new_nsp - > uts_ns ) ;
out_uts :
if ( new_nsp - > mnt_ns )
put_mnt_ns ( new_nsp - > mnt_ns ) ;
out_ns :
2007-07-16 10:41:07 +04:00
kmem_cache_free ( nsproxy_cachep , new_nsp ) ;
2007-07-16 10:41:06 +04:00
return ERR_PTR ( err ) ;
2006-10-02 13:18:06 +04:00
}
/*
* called from clone . This now handles copy for nsproxy and all
* namespaces therein .
*/
2007-07-16 10:41:15 +04:00
int copy_namespaces ( unsigned long flags , struct task_struct * tsk )
2006-10-02 13:18:06 +04:00
{
struct nsproxy * old_ns = tsk - > nsproxy ;
2006-10-02 13:18:08 +04:00
struct nsproxy * new_ns ;
int err = 0 ;
2006-10-02 13:18:06 +04:00
if ( ! old_ns )
return 0 ;
get_nsproxy ( old_ns ) ;
2007-10-19 10:40:10 +04:00
if ( ! ( flags & ( CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
2008-10-16 01:38:45 +04:00
CLONE_NEWPID | CLONE_NEWNET ) ) )
2006-10-02 13:18:08 +04:00
return 0 ;
2007-05-08 11:25:21 +04:00
if ( ! capable ( CAP_SYS_ADMIN ) ) {
err = - EPERM ;
2006-10-02 13:18:08 +04:00
goto out ;
}
2008-04-29 12:01:00 +04:00
/*
* CLONE_NEWIPC must detach from the undolist : after switching
* to a new ipc namespace , the semaphore arrays from the old
* namespace are unreachable . In clone parlance , CLONE_SYSVSEM
* means share undolist with parent , so we must forbid using
* it along with CLONE_NEWIPC .
*/
if ( ( flags & CLONE_NEWIPC ) & & ( flags & CLONE_SYSVSEM ) ) {
err = - EINVAL ;
goto out ;
}
2007-05-08 11:25:21 +04:00
new_ns = create_new_namespaces ( flags , tsk , tsk - > fs ) ;
if ( IS_ERR ( new_ns ) ) {
err = PTR_ERR ( new_ns ) ;
goto out ;
}
2006-12-08 13:37:59 +03:00
2007-05-08 11:25:21 +04:00
tsk - > nsproxy = new_ns ;
2007-10-19 10:39:45 +04:00
2006-10-02 13:18:08 +04:00
out :
2007-01-31 00:35:18 +03:00
put_nsproxy ( old_ns ) ;
2006-10-02 13:18:08 +04:00
return err ;
2006-10-02 13:18:06 +04:00
}
void free_nsproxy ( struct nsproxy * ns )
{
2006-12-08 13:37:59 +03:00
if ( ns - > mnt_ns )
put_mnt_ns ( ns - > mnt_ns ) ;
if ( ns - > uts_ns )
put_uts_ns ( ns - > uts_ns ) ;
if ( ns - > ipc_ns )
put_ipc_ns ( ns - > ipc_ns ) ;
if ( ns - > pid_ns )
put_pid_ns ( ns - > pid_ns ) ;
2007-09-27 09:04:26 +04:00
put_net ( ns - > net_ns ) ;
2007-07-16 10:41:07 +04:00
kmem_cache_free ( nsproxy_cachep , ns ) ;
2006-10-02 13:18:06 +04:00
}
2007-05-08 11:25:21 +04:00
/*
* Called from unshare . Unshare all the namespaces part of nsproxy .
2007-06-24 04:16:25 +04:00
* On success , returns the new nsproxy .
2007-05-08 11:25:21 +04:00
*/
int unshare_nsproxy_namespaces ( unsigned long unshare_flags ,
struct nsproxy * * new_nsp , struct fs_struct * new_fs )
{
int err = 0 ;
2007-07-16 10:41:01 +04:00
if ( ! ( unshare_flags & ( CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
2008-10-16 01:38:45 +04:00
CLONE_NEWNET ) ) )
2007-05-08 11:25:21 +04:00
return 0 ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
* new_nsp = create_new_namespaces ( unshare_flags , current ,
new_fs ? new_fs : current - > fs ) ;
2007-10-19 10:39:45 +04:00
if ( IS_ERR ( * new_nsp ) ) {
2007-05-08 11:25:21 +04:00
err = PTR_ERR ( * new_nsp ) ;
2007-10-19 10:39:45 +04:00
goto out ;
}
2008-07-25 12:47:06 +04:00
err = ns_cgroup_clone ( current , task_pid ( current ) ) ;
2007-10-19 10:39:45 +04:00
if ( err )
put_nsproxy ( * new_nsp ) ;
out :
2007-05-08 11:25:21 +04:00
return err ;
}
2007-07-16 10:41:07 +04:00
2007-10-19 10:39:54 +04:00
void switch_task_namespaces ( struct task_struct * p , struct nsproxy * new )
{
struct nsproxy * ns ;
might_sleep ( ) ;
ns = p - > nsproxy ;
rcu_assign_pointer ( p - > nsproxy , new ) ;
if ( ns & & atomic_dec_and_test ( & ns - > count ) ) {
/*
* wait for others to get what they want from this nsproxy .
*
* cannot release this nsproxy via the call_rcu ( ) since
* put_mnt_ns ( ) will want to sleep
*/
synchronize_rcu ( ) ;
free_nsproxy ( ns ) ;
}
}
void exit_task_namespaces ( struct task_struct * p )
{
switch_task_namespaces ( p , NULL ) ;
}
2007-07-16 10:41:07 +04:00
static int __init nsproxy_cache_init ( void )
{
2007-10-17 10:30:11 +04:00
nsproxy_cachep = KMEM_CACHE ( nsproxy , SLAB_PANIC ) ;
2007-07-16 10:41:07 +04:00
return 0 ;
}
module_init ( nsproxy_cache_init ) ;