2010-03-08 03:41:34 +03:00
# include <linux/proc_fs.h>
# include <linux/nsproxy.h>
# include <linux/ptrace.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
} ;
2015-11-17 18:20:54 +03:00
static const char * proc_ns_get_link ( struct dentry * dentry ,
struct inode * inode , void * * cookie )
2011-06-19 04:48:18 +04:00
{
2014-11-01 18:10:28 +03:00
const struct proc_ns_operations * ns_ops = PROC_I ( inode ) - > ns_ops ;
2011-06-19 04:48:18 +04:00
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 ) ;
2015-11-17 18:20:54 +03:00
if ( ! dentry )
return ERR_PTR ( - ECHILD ) ;
2011-06-19 04:48:18 +04:00
task = get_proc_task ( inode ) ;
if ( ! task )
take the targets of /proc/*/ns/* symlinks to separate fs
New pseudo-filesystem: nsfs. Targets of /proc/*/ns/* live there now.
It's not mountable (not even registered, so it's not in /proc/filesystems,
etc.). Files on it *are* bindable - we explicitly permit that in do_loopback().
This stuff lives in fs/nsfs.c now; proc_ns_fget() moved there as well.
get_proc_ns() is a macro now (it's simply returning ->i_private; would
have been an inline, if not for header ordering headache).
proc_ns_inode() is an ex-parrot. The interface used in procfs is
ns_get_path(path, task, ops) and ns_get_name(buf, size, task, ops).
Dentries and inodes are never hashed; a non-counting reference to dentry
is stashed in ns_common (removed by ->d_prune()) and reused by ns_get_path()
if present. See ns_get_path()/ns_prune_dentry/nsfs_evict() for details
of that mechanism.
As the result, proc_ns_follow_link() has stopped poking in nd->path.mnt;
it does nd_jump_link() on a consistent <vfsmount,dentry> pair it gets
from ns_get_path().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-01 17:57:28 +03:00
return error ;
2011-06-19 04:48:18 +04:00
take the targets of /proc/*/ns/* symlinks to separate fs
New pseudo-filesystem: nsfs. Targets of /proc/*/ns/* live there now.
It's not mountable (not even registered, so it's not in /proc/filesystems,
etc.). Files on it *are* bindable - we explicitly permit that in do_loopback().
This stuff lives in fs/nsfs.c now; proc_ns_fget() moved there as well.
get_proc_ns() is a macro now (it's simply returning ->i_private; would
have been an inline, if not for header ordering headache).
proc_ns_inode() is an ex-parrot. The interface used in procfs is
ns_get_path(path, task, ops) and ns_get_name(buf, size, task, ops).
Dentries and inodes are never hashed; a non-counting reference to dentry
is stashed in ns_common (removed by ->d_prune()) and reused by ns_get_path()
if present. See ns_get_path()/ns_prune_dentry/nsfs_evict() for details
of that mechanism.
As the result, proc_ns_follow_link() has stopped poking in nd->path.mnt;
it does nd_jump_link() on a consistent <vfsmount,dentry> pair it gets
from ns_get_path().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-01 17:57:28 +03:00
if ( ptrace_may_access ( task , PTRACE_MODE_READ ) ) {
error = ns_get_path ( & ns_path , task , ns_ops ) ;
if ( ! error )
2015-05-02 20:37:52 +03:00
nd_jump_link ( & ns_path ) ;
2011-06-19 04:48:18 +04:00
}
put_task_struct ( task ) ;
return error ;
}
static int proc_ns_readlink ( struct dentry * dentry , char __user * buffer , int buflen )
{
2015-03-18 01:25:59 +03:00
struct inode * inode = d_inode ( dentry ) ;
2014-11-01 18:10:28 +03:00
const struct proc_ns_operations * ns_ops = PROC_I ( inode ) - > ns_ops ;
2011-06-19 04:48:18 +04:00
struct task_struct * task ;
char name [ 50 ] ;
2014-03-14 21:42:45 +04:00
int res = - EACCES ;
2011-06-19 04:48:18 +04:00
task = get_proc_task ( inode ) ;
if ( ! task )
take the targets of /proc/*/ns/* symlinks to separate fs
New pseudo-filesystem: nsfs. Targets of /proc/*/ns/* live there now.
It's not mountable (not even registered, so it's not in /proc/filesystems,
etc.). Files on it *are* bindable - we explicitly permit that in do_loopback().
This stuff lives in fs/nsfs.c now; proc_ns_fget() moved there as well.
get_proc_ns() is a macro now (it's simply returning ->i_private; would
have been an inline, if not for header ordering headache).
proc_ns_inode() is an ex-parrot. The interface used in procfs is
ns_get_path(path, task, ops) and ns_get_name(buf, size, task, ops).
Dentries and inodes are never hashed; a non-counting reference to dentry
is stashed in ns_common (removed by ->d_prune()) and reused by ns_get_path()
if present. See ns_get_path()/ns_prune_dentry/nsfs_evict() for details
of that mechanism.
As the result, proc_ns_follow_link() has stopped poking in nd->path.mnt;
it does nd_jump_link() on a consistent <vfsmount,dentry> pair it gets
from ns_get_path().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-01 17:57:28 +03:00
return res ;
2011-06-19 04:48:18 +04:00
take the targets of /proc/*/ns/* symlinks to separate fs
New pseudo-filesystem: nsfs. Targets of /proc/*/ns/* live there now.
It's not mountable (not even registered, so it's not in /proc/filesystems,
etc.). Files on it *are* bindable - we explicitly permit that in do_loopback().
This stuff lives in fs/nsfs.c now; proc_ns_fget() moved there as well.
get_proc_ns() is a macro now (it's simply returning ->i_private; would
have been an inline, if not for header ordering headache).
proc_ns_inode() is an ex-parrot. The interface used in procfs is
ns_get_path(path, task, ops) and ns_get_name(buf, size, task, ops).
Dentries and inodes are never hashed; a non-counting reference to dentry
is stashed in ns_common (removed by ->d_prune()) and reused by ns_get_path()
if present. See ns_get_path()/ns_prune_dentry/nsfs_evict() for details
of that mechanism.
As the result, proc_ns_follow_link() has stopped poking in nd->path.mnt;
it does nd_jump_link() on a consistent <vfsmount,dentry> pair it gets
from ns_get_path().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-01 17:57:28 +03:00
if ( ptrace_may_access ( task , PTRACE_MODE_READ ) ) {
res = ns_get_name ( name , sizeof ( name ) , task , ns_ops ) ;
if ( res > = 0 )
res = readlink_copy ( buffer , buflen , name ) ;
}
2011-06-19 04:48:18 +04:00
put_task_struct ( task ) ;
2014-03-14 21:42:45 +04:00
return res ;
2011-06-19 04:48:18 +04:00
}
static const struct inode_operations proc_ns_link_inode_operations = {
. readlink = proc_ns_readlink ,
2015-11-17 18:20:54 +03:00
. get_link = proc_ns_get_link ,
2011-06-19 04:48:18 +04:00
. setattr = proc_setattr ,
} ;
2013-06-15 11:15:20 +04:00
static int proc_ns_instantiate ( struct inode * dir ,
2010-03-08 03:41:34 +03:00
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 ;
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 ;
2014-11-01 18:10:28 +03:00
ei - > 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 ) )
2013-06-15 11:15:20 +04:00
return 0 ;
2010-03-08 03:41:34 +03:00
out :
2013-06-15 11:15:20 +04:00
return - ENOENT ;
2010-03-08 03:41:34 +03:00
}
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
{
2013-06-15 11:15:20 +04:00
int error ;
2010-03-08 03:41:34 +03:00
struct task_struct * task = get_proc_task ( dir ) ;
const struct proc_ns_operations * * entry , * * last ;
unsigned int len = dentry - > d_name . len ;
2013-06-15 11:15:20 +04:00
error = - ENOENT ;
2010-03-08 03:41:34 +03:00
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 :
2013-06-15 11:15:20 +04:00
return ERR_PTR ( error ) ;
2010-03-08 03:41:34 +03:00
}
const struct inode_operations proc_ns_dir_inode_operations = {
. lookup = proc_ns_dir_lookup ,
. getattr = pid_getattr ,
. setattr = proc_setattr ,
} ;