2011-12-06 21:21:54 +04:00
/*
* fs / proc_namespace . c - handling of / proc / < pid > / { mounts , mountinfo , mountstats }
*
* In fact , that ' s a piece of procfs ; it ' s * almost * isolated from
* the rest of fs / proc , but has rather close relationships with
* fs / namespace . c , thus here instead of fs / proc
*
*/
# include <linux/mnt_namespace.h>
# include <linux/nsproxy.h>
# include <linux/security.h>
# include <linux/fs_struct.h>
# include "proc/internal.h" /* only for get_proc_task() in ->open() */
# include "pnode.h"
# include "internal.h"
static unsigned mounts_poll ( struct file * file , poll_table * wait )
{
2012-06-09 09:16:59 +04:00
struct proc_mounts * p = proc_mounts ( file - > private_data ) ;
2011-12-06 21:21:54 +04:00
struct mnt_namespace * ns = p - > ns ;
unsigned res = POLLIN | POLLRDNORM ;
2013-09-29 18:59:59 +04:00
int event ;
2011-12-06 21:21:54 +04:00
poll_wait ( file , & p - > ns - > poll , wait ) ;
2013-09-29 18:59:59 +04:00
event = ACCESS_ONCE ( ns - > event ) ;
if ( p - > m . poll_event ! = event ) {
p - > m . poll_event = event ;
2011-12-06 21:21:54 +04:00
res | = POLLERR | POLLPRI ;
}
return res ;
}
struct proc_fs_info {
int flag ;
const char * str ;
} ;
static int show_sb_opts ( struct seq_file * m , struct super_block * sb )
{
static const struct proc_fs_info fs_info [ ] = {
{ MS_SYNCHRONOUS , " ,sync " } ,
{ MS_DIRSYNC , " ,dirsync " } ,
{ MS_MANDLOCK , " ,mand " } ,
{ 0 , NULL }
} ;
const struct proc_fs_info * fs_infop ;
for ( fs_infop = fs_info ; fs_infop - > flag ; fs_infop + + ) {
if ( sb - > s_flags & fs_infop - > flag )
seq_puts ( m , fs_infop - > str ) ;
}
return security_sb_show_options ( m , sb ) ;
}
static void show_mnt_opts ( struct seq_file * m , struct vfsmount * mnt )
{
static const struct proc_fs_info mnt_info [ ] = {
{ MNT_NOSUID , " ,nosuid " } ,
{ MNT_NODEV , " ,nodev " } ,
{ MNT_NOEXEC , " ,noexec " } ,
{ MNT_NOATIME , " ,noatime " } ,
{ MNT_NODIRATIME , " ,nodiratime " } ,
{ MNT_RELATIME , " ,relatime " } ,
{ 0 , NULL }
} ;
const struct proc_fs_info * fs_infop ;
for ( fs_infop = mnt_info ; fs_infop - > flag ; fs_infop + + ) {
if ( mnt - > mnt_flags & fs_infop - > flag )
seq_puts ( m , fs_infop - > str ) ;
}
}
static inline void mangle ( struct seq_file * m , const char * s )
{
seq_escape ( m , s , " \t \n \\ " ) ;
}
static void show_type ( struct seq_file * m , struct super_block * sb )
{
mangle ( m , sb - > s_type - > name ) ;
if ( sb - > s_subtype & & sb - > s_subtype [ 0 ] ) {
seq_putc ( m , ' . ' ) ;
mangle ( m , sb - > s_subtype ) ;
}
}
static int show_vfsmnt ( struct seq_file * m , struct vfsmount * mnt )
{
struct mount * r = real_mount ( mnt ) ;
int err = 0 ;
struct path mnt_path = { . dentry = mnt - > mnt_root , . mnt = mnt } ;
2011-12-09 06:32:45 +04:00
struct super_block * sb = mnt_path . dentry - > d_sb ;
2011-12-06 21:21:54 +04:00
2011-12-09 06:32:45 +04:00
if ( sb - > s_op - > show_devname ) {
err = sb - > s_op - > show_devname ( m , mnt_path . dentry ) ;
2011-12-06 21:21:54 +04:00
if ( err )
goto out ;
} else {
mangle ( m , r - > mnt_devname ? r - > mnt_devname : " none " ) ;
}
seq_putc ( m , ' ' ) ;
seq_path ( m , & mnt_path , " \t \n \\ " ) ;
seq_putc ( m , ' ' ) ;
2011-12-09 06:32:45 +04:00
show_type ( m , sb ) ;
2011-12-06 21:21:54 +04:00
seq_puts ( m , __mnt_is_readonly ( mnt ) ? " ro " : " rw " ) ;
2011-12-09 06:32:45 +04:00
err = show_sb_opts ( m , sb ) ;
2011-12-06 21:21:54 +04:00
if ( err )
goto out ;
show_mnt_opts ( m , mnt ) ;
2011-12-09 06:32:45 +04:00
if ( sb - > s_op - > show_options )
2011-12-09 06:32:45 +04:00
err = sb - > s_op - > show_options ( m , mnt_path . dentry ) ;
2011-12-06 21:21:54 +04:00
seq_puts ( m , " 0 0 \n " ) ;
out :
return err ;
}
static int show_mountinfo ( struct seq_file * m , struct vfsmount * mnt )
{
2012-06-09 09:16:59 +04:00
struct proc_mounts * p = proc_mounts ( m ) ;
2011-12-06 21:21:54 +04:00
struct mount * r = real_mount ( mnt ) ;
struct super_block * sb = mnt - > mnt_sb ;
struct path mnt_path = { . dentry = mnt - > mnt_root , . mnt = mnt } ;
struct path root = p - > root ;
int err = 0 ;
seq_printf ( m , " %i %i %u:%u " , r - > mnt_id , r - > mnt_parent - > mnt_id ,
MAJOR ( sb - > s_dev ) , MINOR ( sb - > s_dev ) ) ;
if ( sb - > s_op - > show_path )
2011-12-09 06:37:57 +04:00
err = sb - > s_op - > show_path ( m , mnt - > mnt_root ) ;
2011-12-06 21:21:54 +04:00
else
seq_dentry ( m , mnt - > mnt_root , " \t \n \\ " ) ;
if ( err )
goto out ;
seq_putc ( m , ' ' ) ;
/* mountpoints outside of chroot jail will give SEQ_SKIP on this */
err = seq_path_root ( m , & mnt_path , & root , " \t \n \\ " ) ;
if ( err )
goto out ;
seq_puts ( m , mnt - > mnt_flags & MNT_READONLY ? " ro " : " rw " ) ;
show_mnt_opts ( m , mnt ) ;
/* Tagged fields ("foo:X" or "bar") */
if ( IS_MNT_SHARED ( r ) )
seq_printf ( m , " shared:%i " , r - > mnt_group_id ) ;
if ( IS_MNT_SLAVE ( r ) ) {
int master = r - > mnt_master - > mnt_group_id ;
int dom = get_dominating_id ( r , & p - > root ) ;
seq_printf ( m , " master:%i " , master ) ;
if ( dom & & dom ! = master )
seq_printf ( m , " propagate_from:%i " , dom ) ;
}
if ( IS_MNT_UNBINDABLE ( r ) )
seq_puts ( m , " unbindable " ) ;
/* Filesystem specific data */
seq_puts ( m , " - " ) ;
show_type ( m , sb ) ;
seq_putc ( m , ' ' ) ;
if ( sb - > s_op - > show_devname )
2011-12-09 06:32:45 +04:00
err = sb - > s_op - > show_devname ( m , mnt - > mnt_root ) ;
2011-12-06 21:21:54 +04:00
else
mangle ( m , r - > mnt_devname ? r - > mnt_devname : " none " ) ;
if ( err )
goto out ;
seq_puts ( m , sb - > s_flags & MS_RDONLY ? " ro " : " rw " ) ;
err = show_sb_opts ( m , sb ) ;
if ( err )
goto out ;
if ( sb - > s_op - > show_options )
2011-12-09 06:32:45 +04:00
err = sb - > s_op - > show_options ( m , mnt - > mnt_root ) ;
2011-12-06 21:21:54 +04:00
seq_putc ( m , ' \n ' ) ;
out :
return err ;
}
static int show_vfsstat ( struct seq_file * m , struct vfsmount * mnt )
{
struct mount * r = real_mount ( mnt ) ;
struct path mnt_path = { . dentry = mnt - > mnt_root , . mnt = mnt } ;
2011-12-09 05:51:13 +04:00
struct super_block * sb = mnt_path . dentry - > d_sb ;
2011-12-06 21:21:54 +04:00
int err = 0 ;
/* device */
2011-12-09 05:51:13 +04:00
if ( sb - > s_op - > show_devname ) {
2011-12-06 21:21:54 +04:00
seq_puts ( m , " device " ) ;
2011-12-09 06:32:45 +04:00
err = sb - > s_op - > show_devname ( m , mnt_path . dentry ) ;
2011-12-06 21:21:54 +04:00
} else {
if ( r - > mnt_devname ) {
seq_puts ( m , " device " ) ;
mangle ( m , r - > mnt_devname ) ;
} else
seq_puts ( m , " no device " ) ;
}
/* mount point */
seq_puts ( m , " mounted on " ) ;
seq_path ( m , & mnt_path , " \t \n \\ " ) ;
seq_putc ( m , ' ' ) ;
/* file system type */
seq_puts ( m , " with fstype " ) ;
2011-12-09 05:51:13 +04:00
show_type ( m , sb ) ;
2011-12-06 21:21:54 +04:00
/* optional statistics */
2011-12-09 05:51:13 +04:00
if ( sb - > s_op - > show_stats ) {
2011-12-06 21:21:54 +04:00
seq_putc ( m , ' ' ) ;
if ( ! err )
2011-12-09 05:51:13 +04:00
err = sb - > s_op - > show_stats ( m , mnt_path . dentry ) ;
2011-12-06 21:21:54 +04:00
}
seq_putc ( m , ' \n ' ) ;
return err ;
}
static int mounts_open_common ( struct inode * inode , struct file * file ,
int ( * show ) ( struct seq_file * , struct vfsmount * ) )
{
struct task_struct * task = get_proc_task ( inode ) ;
struct nsproxy * nsp ;
struct mnt_namespace * ns = NULL ;
struct path root ;
struct proc_mounts * p ;
int ret = - EINVAL ;
if ( ! task )
goto err ;
rcu_read_lock ( ) ;
nsp = task_nsproxy ( task ) ;
2014-01-24 03:55:44 +04:00
if ( ! nsp | | ! nsp - > mnt_ns ) {
2011-12-06 21:21:54 +04:00
rcu_read_unlock ( ) ;
put_task_struct ( task ) ;
goto err ;
}
ns = nsp - > mnt_ns ;
get_mnt_ns ( ns ) ;
rcu_read_unlock ( ) ;
task_lock ( task ) ;
if ( ! task - > fs ) {
task_unlock ( task ) ;
put_task_struct ( task ) ;
ret = - ENOENT ;
goto err_put_ns ;
}
get_fs_root ( task - > fs , & root ) ;
task_unlock ( task ) ;
put_task_struct ( task ) ;
ret = - ENOMEM ;
p = kmalloc ( sizeof ( struct proc_mounts ) , GFP_KERNEL ) ;
if ( ! p )
goto err_put_path ;
file - > private_data = & p - > m ;
ret = seq_open ( file , & mounts_op ) ;
if ( ret )
goto err_free ;
p - > ns = ns ;
p - > root = root ;
p - > m . poll_event = ns - > event ;
p - > show = show ;
2014-02-27 23:40:10 +04:00
p - > cached_event = ~ 0ULL ;
2011-12-06 21:21:54 +04:00
return 0 ;
err_free :
kfree ( p ) ;
err_put_path :
path_put ( & root ) ;
err_put_ns :
put_mnt_ns ( ns ) ;
err :
return ret ;
}
static int mounts_release ( struct inode * inode , struct file * file )
{
2012-06-09 09:16:59 +04:00
struct proc_mounts * p = proc_mounts ( file - > private_data ) ;
2011-12-06 21:21:54 +04:00
path_put ( & p - > root ) ;
put_mnt_ns ( p - > ns ) ;
return seq_release ( inode , file ) ;
}
static int mounts_open ( struct inode * inode , struct file * file )
{
return mounts_open_common ( inode , file , show_vfsmnt ) ;
}
static int mountinfo_open ( struct inode * inode , struct file * file )
{
return mounts_open_common ( inode , file , show_mountinfo ) ;
}
static int mountstats_open ( struct inode * inode , struct file * file )
{
return mounts_open_common ( inode , file , show_vfsstat ) ;
}
const struct file_operations proc_mounts_operations = {
. open = mounts_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = mounts_release ,
. poll = mounts_poll ,
} ;
const struct file_operations proc_mountinfo_operations = {
. open = mountinfo_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = mounts_release ,
. poll = mounts_poll ,
} ;
const struct file_operations proc_mountstats_operations = {
. open = mountstats_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = mounts_release ,
} ;