2005-04-16 15:20:36 -07:00
/* -*- linux-c -*- --------------------------------------------------------- *
*
* linux / fs / devpts / inode . c
*
* Copyright 1998 - 2004 H . Peter Anvin - - All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License , version 2 , or at your
* option , any later version , incorporated herein by reference .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include <linux/module.h>
# include <linux/init.h>
# include <linux/fs.h>
# include <linux/sched.h>
# include <linux/namei.h>
# include <linux/mount.h>
# include <linux/tty.h>
# include <linux/devpts_fs.h>
# define DEVPTS_SUPER_MAGIC 0x1cd1
static struct vfsmount * devpts_mnt ;
static struct dentry * devpts_root ;
static struct {
int setuid ;
int setgid ;
uid_t uid ;
gid_t gid ;
umode_t mode ;
} config = { . mode = 0600 } ;
static int devpts_remount ( struct super_block * sb , int * flags , char * data )
{
int setuid = 0 ;
int setgid = 0 ;
uid_t uid = 0 ;
gid_t gid = 0 ;
umode_t mode = 0600 ;
char * this_char ;
this_char = NULL ;
while ( ( this_char = strsep ( & data , " , " ) ) ! = NULL ) {
int n ;
char dummy ;
if ( ! * this_char )
continue ;
if ( sscanf ( this_char , " uid=%i%c " , & n , & dummy ) = = 1 ) {
setuid = 1 ;
uid = n ;
} else if ( sscanf ( this_char , " gid=%i%c " , & n , & dummy ) = = 1 ) {
setgid = 1 ;
gid = n ;
} else if ( sscanf ( this_char , " mode=%o%c " , & n , & dummy ) = = 1 )
mode = n & ~ S_IFMT ;
else {
printk ( " devpts: called with bogus options \n " ) ;
return - EINVAL ;
}
}
config . setuid = setuid ;
config . setgid = setgid ;
config . uid = uid ;
config . gid = gid ;
config . mode = mode ;
return 0 ;
}
static struct super_operations devpts_sops = {
. statfs = simple_statfs ,
. remount_fs = devpts_remount ,
} ;
static int
devpts_fill_super ( struct super_block * s , void * data , int silent )
{
struct inode * inode ;
s - > s_blocksize = 1024 ;
s - > s_blocksize_bits = 10 ;
s - > s_magic = DEVPTS_SUPER_MAGIC ;
s - > s_op = & devpts_sops ;
s - > s_time_gran = 1 ;
inode = new_inode ( s ) ;
if ( ! inode )
goto fail ;
inode - > i_ino = 1 ;
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME ;
inode - > i_blocks = 0 ;
inode - > i_blksize = 1024 ;
inode - > i_uid = inode - > i_gid = 0 ;
inode - > i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR ;
inode - > i_op = & simple_dir_inode_operations ;
inode - > i_fop = & simple_dir_operations ;
inode - > i_nlink = 2 ;
devpts_root = s - > s_root = d_alloc_root ( inode ) ;
if ( s - > s_root )
return 0 ;
printk ( " devpts: get root dentry failed \n " ) ;
iput ( inode ) ;
fail :
return - ENOMEM ;
}
static struct super_block * devpts_get_sb ( struct file_system_type * fs_type ,
int flags , const char * dev_name , void * data )
{
return get_sb_single ( fs_type , flags , data , devpts_fill_super ) ;
}
static struct file_system_type devpts_fs_type = {
. owner = THIS_MODULE ,
. name = " devpts " ,
. get_sb = devpts_get_sb ,
. kill_sb = kill_anon_super ,
} ;
/*
* The normal naming convention is simply / dev / pts / < number > ; this conforms
* to the System V naming convention
*/
static struct dentry * get_node ( int num )
{
char s [ 12 ] ;
struct dentry * root = devpts_root ;
2006-01-09 15:59:24 -08:00
mutex_lock ( & root - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
return lookup_one_len ( s , root , sprintf ( s , " %d " , num ) ) ;
}
int devpts_pty_new ( struct tty_struct * tty )
{
int number = tty - > index ;
struct tty_driver * driver = tty - > driver ;
dev_t device = MKDEV ( driver - > major , driver - > minor_start + number ) ;
struct dentry * dentry ;
struct inode * inode = new_inode ( devpts_mnt - > mnt_sb ) ;
/* We're supposed to be given the slave end of a pty */
BUG_ON ( driver - > type ! = TTY_DRIVER_TYPE_PTY ) ;
BUG_ON ( driver - > subtype ! = PTY_TYPE_SLAVE ) ;
if ( ! inode )
return - ENOMEM ;
inode - > i_ino = number + 2 ;
inode - > i_blksize = 1024 ;
inode - > i_uid = config . setuid ? config . uid : current - > fsuid ;
inode - > i_gid = config . setgid ? config . gid : current - > fsgid ;
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME ;
init_special_inode ( inode , S_IFCHR | config . mode , device ) ;
inode - > u . generic_ip = tty ;
dentry = get_node ( number ) ;
if ( ! IS_ERR ( dentry ) & & ! dentry - > d_inode )
d_instantiate ( dentry , inode ) ;
2006-01-09 15:59:24 -08:00
mutex_unlock ( & devpts_root - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
struct tty_struct * devpts_get_tty ( int number )
{
struct dentry * dentry = get_node ( number ) ;
struct tty_struct * tty ;
tty = NULL ;
if ( ! IS_ERR ( dentry ) ) {
if ( dentry - > d_inode )
tty = dentry - > d_inode - > u . generic_ip ;
dput ( dentry ) ;
}
2006-01-09 15:59:24 -08:00
mutex_unlock ( & devpts_root - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
return tty ;
}
void devpts_pty_kill ( int number )
{
struct dentry * dentry = get_node ( number ) ;
if ( ! IS_ERR ( dentry ) ) {
struct inode * inode = dentry - > d_inode ;
if ( inode ) {
inode - > i_nlink - - ;
d_delete ( dentry ) ;
dput ( dentry ) ;
}
dput ( dentry ) ;
}
2006-01-09 15:59:24 -08:00
mutex_unlock ( & devpts_root - > d_inode - > i_mutex ) ;
2005-04-16 15:20:36 -07:00
}
static int __init init_devpts_fs ( void )
{
int err = register_filesystem ( & devpts_fs_type ) ;
if ( ! err ) {
devpts_mnt = kern_mount ( & devpts_fs_type ) ;
if ( IS_ERR ( devpts_mnt ) )
err = PTR_ERR ( devpts_mnt ) ;
}
return err ;
}
static void __exit exit_devpts_fs ( void )
{
unregister_filesystem ( & devpts_fs_type ) ;
mntput ( devpts_mnt ) ;
}
module_init ( init_devpts_fs )
module_exit ( exit_devpts_fs )
MODULE_LICENSE ( " GPL " ) ;