2010-03-08 03:41:34 +03:00
# include <linux/proc_fs.h>
# include <linux/nsproxy.h>
# include <linux/sched.h>
# include <linux/ptrace.h>
# include <linux/fs_struct.h>
# include <linux/mount.h>
# include <linux/path.h>
# include <linux/namei.h>
# include <linux/file.h>
# include <linux/utsname.h>
# include <net/net_namespace.h>
# include <linux/ipc_namespace.h>
# include <linux/pid_namespace.h>
2012-07-26 17:24:06 +04:00
# include <linux/user_namespace.h>
2010-03-08 03:41:34 +03:00
# include "internal.h"
static const struct proc_ns_operations * ns_entries [ ] = {
2010-03-08 05:14:23 +03:00
# ifdef CONFIG_NET_NS
& netns_operations ,
# endif
2010-03-08 05:43:27 +03:00
# ifdef CONFIG_UTS_NS
& utsns_operations ,
# endif
2010-03-08 05:48:39 +03:00
# ifdef CONFIG_IPC_NS
& ipcns_operations ,
# endif
2010-03-08 05:17:03 +03:00
# ifdef CONFIG_PID_NS
& pidns_operations ,
2012-07-26 17:24:06 +04:00
# endif
# ifdef CONFIG_USER_NS
& userns_operations ,
2010-03-08 05:17:03 +03:00
# endif
2010-03-08 05:49:36 +03:00
& mntns_operations ,
2010-03-08 03:41:34 +03:00
} ;
static const struct file_operations ns_file_operations = {
. llseek = no_llseek ,
} ;
2011-06-19 04:48:18 +04:00
static const struct inode_operations ns_inode_operations = {
. setattr = proc_setattr ,
} ;
static int ns_delete_dentry ( const struct dentry * dentry )
{
/* Don't cache namespace inodes when not in use */
return 1 ;
}
static char * ns_dname ( struct dentry * dentry , char * buffer , int buflen )
{
struct inode * inode = dentry - > d_inode ;
2013-04-12 04:50:06 +04:00
const struct proc_ns_operations * ns_ops = PROC_I ( inode ) - > ns . ns_ops ;
2011-06-19 04:48:18 +04:00
return dynamic_dname ( dentry , buffer , buflen , " %s:[%lu] " ,
ns_ops - > name , inode - > i_ino ) ;
}
const struct dentry_operations ns_dentry_operations =
{
. d_delete = ns_delete_dentry ,
. d_dname = ns_dname ,
} ;
static struct dentry * proc_ns_get_dentry ( struct super_block * sb ,
struct task_struct * task , const struct proc_ns_operations * ns_ops )
{
struct dentry * dentry , * result ;
struct inode * inode ;
struct proc_inode * ei ;
struct qstr qname = { . name = " " , } ;
void * ns ;
ns = ns_ops - > get ( task ) ;
if ( ! ns )
return ERR_PTR ( - ENOENT ) ;
dentry = d_alloc_pseudo ( sb , & qname ) ;
if ( ! dentry ) {
ns_ops - > put ( ns ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2011-06-15 21:21:48 +04:00
inode = iget_locked ( sb , ns_ops - > inum ( ns ) ) ;
2011-06-19 04:48:18 +04:00
if ( ! inode ) {
dput ( dentry ) ;
ns_ops - > put ( ns ) ;
return ERR_PTR ( - ENOMEM ) ;
}
ei = PROC_I ( inode ) ;
2011-06-15 21:21:48 +04:00
if ( inode - > i_state & I_NEW ) {
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME ;
inode - > i_op = & ns_inode_operations ;
inode - > i_mode = S_IFREG | S_IRUGO ;
inode - > i_fop = & ns_file_operations ;
2013-04-12 04:50:06 +04:00
ei - > ns . ns_ops = ns_ops ;
ei - > ns . ns = ns ;
2011-06-15 21:21:48 +04:00
unlock_new_inode ( inode ) ;
} else {
ns_ops - > put ( ns ) ;
}
2011-06-19 04:48:18 +04:00
d_set_d_op ( dentry , & ns_dentry_operations ) ;
result = d_instantiate_unique ( dentry , inode ) ;
if ( result ) {
dput ( dentry ) ;
dentry = result ;
}
return dentry ;
}
static void * proc_ns_follow_link ( struct dentry * dentry , struct nameidata * nd )
{
struct inode * inode = dentry - > d_inode ;
struct super_block * sb = inode - > i_sb ;
struct proc_inode * ei = PROC_I ( inode ) ;
struct task_struct * task ;
2013-03-09 12:14:45 +04:00
struct path ns_path ;
2011-06-19 04:48:18 +04:00
void * error = ERR_PTR ( - EACCES ) ;
task = get_proc_task ( inode ) ;
if ( ! task )
goto out ;
if ( ! ptrace_may_access ( task , PTRACE_MODE_READ ) )
goto out_put_task ;
2013-04-12 04:50:06 +04:00
ns_path . dentry = proc_ns_get_dentry ( sb , task , ei - > ns . ns_ops ) ;
2013-03-09 12:14:45 +04:00
if ( IS_ERR ( ns_path . dentry ) ) {
error = ERR_CAST ( ns_path . dentry ) ;
2011-06-19 04:48:18 +04:00
goto out_put_task ;
}
2013-03-09 12:14:45 +04:00
ns_path . mnt = mntget ( nd - > path . mnt ) ;
nd_jump_link ( nd , & ns_path ) ;
2011-06-19 04:48:18 +04:00
error = NULL ;
out_put_task :
put_task_struct ( task ) ;
out :
return error ;
}
static int proc_ns_readlink ( struct dentry * dentry , char __user * buffer , int buflen )
{
struct inode * inode = dentry - > d_inode ;
struct proc_inode * ei = PROC_I ( inode ) ;
2013-04-12 04:50:06 +04:00
const struct proc_ns_operations * ns_ops = ei - > ns . ns_ops ;
2011-06-19 04:48:18 +04:00
struct task_struct * task ;
void * ns ;
char name [ 50 ] ;
int len = - EACCES ;
task = get_proc_task ( inode ) ;
if ( ! task )
goto out ;
if ( ! ptrace_may_access ( task , PTRACE_MODE_READ ) )
goto out_put_task ;
len = - ENOENT ;
ns = ns_ops - > get ( task ) ;
if ( ! ns )
goto out_put_task ;
2011-06-15 21:21:48 +04:00
snprintf ( name , sizeof ( name ) , " %s:[%u] " , ns_ops - > name , ns_ops - > inum ( ns ) ) ;
2011-06-19 04:48:18 +04:00
len = strlen ( name ) ;
if ( len > buflen )
len = buflen ;
2011-06-15 21:21:48 +04:00
if ( copy_to_user ( buffer , name , len ) )
2011-06-19 04:48:18 +04:00
len = - EFAULT ;
ns_ops - > put ( ns ) ;
out_put_task :
put_task_struct ( task ) ;
out :
return len ;
}
static const struct inode_operations proc_ns_link_inode_operations = {
. readlink = proc_ns_readlink ,
. follow_link = proc_ns_follow_link ,
. setattr = proc_setattr ,
} ;
2010-03-08 03:41:34 +03:00
static struct dentry * proc_ns_instantiate ( struct inode * dir ,
struct dentry * dentry , struct task_struct * task , const void * ptr )
{
const struct proc_ns_operations * ns_ops = ptr ;
struct inode * inode ;
struct proc_inode * ei ;
struct dentry * error = ERR_PTR ( - ENOENT ) ;
inode = proc_pid_make_inode ( dir - > i_sb , task ) ;
if ( ! inode )
goto out ;
ei = PROC_I ( inode ) ;
2011-06-19 04:48:18 +04:00
inode - > i_mode = S_IFLNK | S_IRWXUGO ;
inode - > i_op = & proc_ns_link_inode_operations ;
2013-04-12 04:50:06 +04:00
ei - > ns . ns_ops = ns_ops ;
2010-03-08 03:41:34 +03:00
2012-03-24 02:02:55 +04:00
d_set_d_op ( dentry , & pid_dentry_operations ) ;
2010-03-08 03:41:34 +03:00
d_add ( dentry , inode ) ;
/* Close the race of the process dying before we return the dentry */
2012-06-11 00:03:43 +04:00
if ( pid_revalidate ( dentry , 0 ) )
2010-03-08 03:41:34 +03:00
error = NULL ;
out :
return error ;
}
2013-05-16 20:07:31 +04:00
static int proc_ns_dir_readdir ( struct file * file , struct dir_context * ctx )
2010-03-08 03:41:34 +03:00
{
2013-05-16 20:07:31 +04:00
struct task_struct * task = get_proc_task ( file_inode ( file ) ) ;
2010-03-08 03:41:34 +03:00
const struct proc_ns_operations * * entry , * * last ;
if ( ! task )
2013-05-16 20:07:31 +04:00
return - ENOENT ;
2010-03-08 03:41:34 +03:00
2013-05-16 20:07:31 +04:00
if ( ! dir_emit_dots ( file , ctx ) )
goto out ;
if ( ctx - > pos > = 2 + ARRAY_SIZE ( ns_entries ) )
goto out ;
entry = ns_entries + ( ctx - > pos - 2 ) ;
last = & ns_entries [ ARRAY_SIZE ( ns_entries ) - 1 ] ;
while ( entry < = last ) {
const struct proc_ns_operations * ops = * entry ;
if ( ! proc_fill_cache ( file , ctx , ops - > name , strlen ( ops - > name ) ,
proc_ns_instantiate , task , ops ) )
break ;
ctx - > pos + + ;
entry + + ;
}
2010-03-08 03:41:34 +03:00
out :
put_task_struct ( task ) ;
2013-05-16 20:07:31 +04:00
return 0 ;
2010-03-08 03:41:34 +03:00
}
const struct file_operations proc_ns_dir_operations = {
. read = generic_read_dir ,
2013-05-16 20:07:31 +04:00
. iterate = proc_ns_dir_readdir ,
2010-03-08 03:41:34 +03:00
} ;
static struct dentry * proc_ns_dir_lookup ( struct inode * dir ,
2012-06-11 01:13:09 +04:00
struct dentry * dentry , unsigned int flags )
2010-03-08 03:41:34 +03:00
{
struct dentry * error ;
struct task_struct * task = get_proc_task ( dir ) ;
const struct proc_ns_operations * * entry , * * last ;
unsigned int len = dentry - > d_name . len ;
error = ERR_PTR ( - ENOENT ) ;
if ( ! task )
goto out_no_task ;
2012-03-29 01:42:52 +04:00
last = & ns_entries [ ARRAY_SIZE ( ns_entries ) ] ;
for ( entry = ns_entries ; entry < last ; entry + + ) {
2010-03-08 03:41:34 +03:00
if ( strlen ( ( * entry ) - > name ) ! = len )
continue ;
if ( ! memcmp ( dentry - > d_name . name , ( * entry ) - > name , len ) )
break ;
}
2012-03-29 01:42:52 +04:00
if ( entry = = last )
2010-03-08 03:41:34 +03:00
goto out ;
error = proc_ns_instantiate ( dir , dentry , task , * entry ) ;
out :
put_task_struct ( task ) ;
out_no_task :
return error ;
}
const struct inode_operations proc_ns_dir_inode_operations = {
. lookup = proc_ns_dir_lookup ,
. getattr = pid_getattr ,
. setattr = proc_setattr ,
} ;
struct file * proc_ns_fget ( int fd )
{
struct file * file ;
file = fget ( fd ) ;
if ( ! file )
return ERR_PTR ( - EBADF ) ;
if ( file - > f_op ! = & ns_file_operations )
goto out_invalid ;
return file ;
out_invalid :
fput ( file ) ;
return ERR_PTR ( - EINVAL ) ;
}
2013-04-12 04:50:06 +04:00
struct proc_ns * get_proc_ns ( struct inode * inode )
{
return & PROC_I ( inode ) - > ns ;
}
2010-03-08 05:49:36 +03:00
bool proc_ns_inode ( struct inode * inode )
{
return inode - > i_fop = = & ns_file_operations ;
}