2006-06-25 16:48:51 +04:00
/*
FUSE : Filesystem in Userspace
Copyright ( C ) 2001 - 2006 Miklos Szeredi < miklos @ szeredi . hu >
This program can be distributed under the terms of the GNU GPL .
See the file COPYING .
*/
# include "fuse_i.h"
# include <linux/init.h>
# include <linux/module.h>
# define FUSE_CTL_SUPER_MAGIC 0x65735543
/*
* This is non - NULL when the single instance of the control filesystem
* exists . Protected by fuse_mutex
*/
static struct super_block * fuse_control_sb ;
static struct fuse_conn * fuse_ctl_file_conn_get ( struct file * file )
{
struct fuse_conn * fc ;
mutex_lock ( & fuse_mutex ) ;
2006-12-08 13:37:02 +03:00
fc = file - > f_path . dentry - > d_inode - > i_private ;
2006-06-25 16:48:51 +04:00
if ( fc )
fc = fuse_conn_get ( fc ) ;
mutex_unlock ( & fuse_mutex ) ;
return fc ;
}
static ssize_t fuse_conn_abort_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
struct fuse_conn * fc = fuse_ctl_file_conn_get ( file ) ;
if ( fc ) {
fuse_abort_conn ( fc ) ;
fuse_conn_put ( fc ) ;
}
return count ;
}
static ssize_t fuse_conn_waiting_read ( struct file * file , char __user * buf ,
size_t len , loff_t * ppos )
{
char tmp [ 32 ] ;
size_t size ;
if ( ! * ppos ) {
struct fuse_conn * fc = fuse_ctl_file_conn_get ( file ) ;
if ( ! fc )
return 0 ;
file - > private_data = ( void * ) ( long ) atomic_read ( & fc - > num_waiting ) ;
fuse_conn_put ( fc ) ;
}
size = sprintf ( tmp , " %ld \n " , ( long ) file - > private_data ) ;
return simple_read_from_buffer ( buf , len , ppos , tmp , size ) ;
}
static const struct file_operations fuse_ctl_abort_ops = {
. open = nonseekable_open ,
. write = fuse_conn_abort_write ,
} ;
static const struct file_operations fuse_ctl_waiting_ops = {
. open = nonseekable_open ,
. read = fuse_conn_waiting_read ,
} ;
static struct dentry * fuse_ctl_add_dentry ( struct dentry * parent ,
struct fuse_conn * fc ,
const char * name ,
int mode , int nlink ,
2007-02-12 11:55:38 +03:00
const struct inode_operations * iop ,
2006-06-25 16:48:51 +04:00
const struct file_operations * fop )
{
struct dentry * dentry ;
struct inode * inode ;
BUG_ON ( fc - > ctl_ndents > = FUSE_CTL_NUM_DENTRIES ) ;
dentry = d_alloc_name ( parent , name ) ;
if ( ! dentry )
return NULL ;
fc - > ctl_dentry [ fc - > ctl_ndents + + ] = dentry ;
inode = new_inode ( fuse_control_sb ) ;
if ( ! inode )
return NULL ;
inode - > i_mode = mode ;
inode - > i_uid = fc - > user_id ;
inode - > i_gid = fc - > group_id ;
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = CURRENT_TIME ;
/* setting ->i_op to NULL is not allowed */
if ( iop )
inode - > i_op = iop ;
inode - > i_fop = fop ;
inode - > i_nlink = nlink ;
2006-09-27 12:50:46 +04:00
inode - > i_private = fc ;
2006-06-25 16:48:51 +04:00
d_add ( dentry , inode ) ;
return dentry ;
}
/*
* Add a connection to the control filesystem ( if it exists ) . Caller
2006-07-30 14:04:10 +04:00
* must hold fuse_mutex
2006-06-25 16:48:51 +04:00
*/
int fuse_ctl_add_conn ( struct fuse_conn * fc )
{
struct dentry * parent ;
char name [ 32 ] ;
if ( ! fuse_control_sb )
return 0 ;
parent = fuse_control_sb - > s_root ;
2006-10-01 10:29:04 +04:00
inc_nlink ( parent - > d_inode ) ;
2008-04-30 11:54:34 +04:00
sprintf ( name , " %u " , fc - > dev ) ;
2006-06-25 16:48:51 +04:00
parent = fuse_ctl_add_dentry ( parent , fc , name , S_IFDIR | 0500 , 2 ,
& simple_dir_inode_operations ,
& simple_dir_operations ) ;
if ( ! parent )
goto err ;
if ( ! fuse_ctl_add_dentry ( parent , fc , " waiting " , S_IFREG | 0400 , 1 ,
NULL , & fuse_ctl_waiting_ops ) | |
! fuse_ctl_add_dentry ( parent , fc , " abort " , S_IFREG | 0200 , 1 ,
NULL , & fuse_ctl_abort_ops ) )
goto err ;
return 0 ;
err :
fuse_ctl_remove_conn ( fc ) ;
return - ENOMEM ;
}
/*
* Remove a connection from the control filesystem ( if it exists ) .
2006-07-30 14:04:10 +04:00
* Caller must hold fuse_mutex
2006-06-25 16:48:51 +04:00
*/
void fuse_ctl_remove_conn ( struct fuse_conn * fc )
{
int i ;
if ( ! fuse_control_sb )
return ;
for ( i = fc - > ctl_ndents - 1 ; i > = 0 ; i - - ) {
struct dentry * dentry = fc - > ctl_dentry [ i ] ;
2006-09-27 12:50:46 +04:00
dentry - > d_inode - > i_private = NULL ;
2006-06-25 16:48:51 +04:00
d_drop ( dentry ) ;
dput ( dentry ) ;
}
fuse_control_sb - > s_root - > d_inode - > i_nlink - - ;
}
static int fuse_ctl_fill_super ( struct super_block * sb , void * data , int silent )
{
struct tree_descr empty_descr = { " " } ;
struct fuse_conn * fc ;
int err ;
err = simple_fill_super ( sb , FUSE_CTL_SUPER_MAGIC , & empty_descr ) ;
if ( err )
return err ;
mutex_lock ( & fuse_mutex ) ;
BUG_ON ( fuse_control_sb ) ;
fuse_control_sb = sb ;
list_for_each_entry ( fc , & fuse_conn_list , entry ) {
err = fuse_ctl_add_conn ( fc ) ;
if ( err ) {
fuse_control_sb = NULL ;
mutex_unlock ( & fuse_mutex ) ;
return err ;
}
}
mutex_unlock ( & fuse_mutex ) ;
return 0 ;
}
static int fuse_ctl_get_sb ( struct file_system_type * fs_type , int flags ,
const char * dev_name , void * raw_data ,
struct vfsmount * mnt )
{
return get_sb_single ( fs_type , flags , raw_data ,
fuse_ctl_fill_super , mnt ) ;
}
static void fuse_ctl_kill_sb ( struct super_block * sb )
{
2007-01-30 00:19:54 +03:00
struct fuse_conn * fc ;
2006-06-25 16:48:51 +04:00
mutex_lock ( & fuse_mutex ) ;
fuse_control_sb = NULL ;
2007-01-30 00:19:54 +03:00
list_for_each_entry ( fc , & fuse_conn_list , entry )
fc - > ctl_ndents = 0 ;
2006-06-25 16:48:51 +04:00
mutex_unlock ( & fuse_mutex ) ;
kill_litter_super ( sb ) ;
}
static struct file_system_type fuse_ctl_fs_type = {
. owner = THIS_MODULE ,
. name = " fusectl " ,
. get_sb = fuse_ctl_get_sb ,
. kill_sb = fuse_ctl_kill_sb ,
} ;
int __init fuse_ctl_init ( void )
{
return register_filesystem ( & fuse_ctl_fs_type ) ;
}
void fuse_ctl_cleanup ( void )
{
unregister_filesystem ( & fuse_ctl_fs_type ) ;
}