2001-08-20 12:03:02 +04:00
/*
* dm . c
*
* Copyright ( C ) 2001 Sistina Software
*
* This software is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 , or ( at
* your option ) any later version .
*
* This software is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with GNU CC ; see the file COPYING . If not , write to
* the Free Software Foundation , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
*/
/*
* procfs and devfs handling for device mapper
*
* Changelog
*
* 16 / 08 / 2001 - First version [ Joe Thornber ]
*/
# include "dm.h"
2001-08-21 19:24:02 +04:00
# include <linux/proc_fs.h>
# include <linux/ctype.h>
2001-08-20 12:03:02 +04:00
/*
* / dev / device - mapper / control is the control char device used to
* create / destroy mapping devices .
*
* When a mapping device called < name > is created it appears as
* / dev / device - mapper / < name > . In addition the interface to control the
* mapping will appear in / proc / device - mapper / < name > .
*/
const char * _fs_dir = " device-mapper " ;
2001-08-20 17:45:43 +04:00
const char * _control_name = " control " ;
2001-08-20 12:03:02 +04:00
static struct proc_dir_entry * _proc_dir ;
2001-08-20 17:45:43 +04:00
static struct proc_dir_entry * _control ;
2001-08-20 12:03:02 +04:00
static devfs_handle_t _dev_dir ;
2001-08-20 17:45:43 +04:00
static int _line_splitter ( struct file * file , const char * buffer ,
2001-08-21 19:24:02 +04:00
unsigned long count , void * data ) ;
int _process_control ( const char * b , const char * e , int minor ) ;
static int _process_table ( const char * b , const char * e , int minor ) ;
static const char * _eat_space ( const char * b , const char * e ) ;
static int _get_word ( const char * b , const char * e ,
const char * * wb , const char * * we ) ;
static int _tok_cmp ( const char * str , const char * b , const char * e ) ;
static void _tok_cpy ( char * dest , size_t max ,
const char * b , const char * e ) ;
2001-08-20 17:45:43 +04:00
2001-08-21 19:24:02 +04:00
typedef int ( * process_fn ) ( const char * b , const char * e , int minor ) ;
2001-08-20 17:45:43 +04:00
struct pf_data {
2001-08-21 19:24:02 +04:00
process_fn fn ;
2001-08-20 17:45:43 +04:00
int minor ;
} ;
2001-08-20 12:03:02 +04:00
int dm_init_fs ( )
{
2001-08-20 17:45:43 +04:00
struct pf_data * pfd = kmalloc ( sizeof ( * pfd ) , GFP_KERNEL ) ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
if ( ! pfd )
2001-08-20 12:03:02 +04:00
return 0 ;
2001-08-20 17:45:43 +04:00
_dev_dir = devfs_mk_dir ( 0 , _fs_dir , NULL ) ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
if ( ! ( _proc_dir = create_proc_entry ( _fs_dir , S_IFDIR , & proc_root ) ) )
goto fail ;
2001-08-20 12:03:02 +04:00
2001-08-23 00:10:06 +04:00
if ( ! ( _control = create_proc_entry ( _control_name , S_IWUSR , _proc_dir ) ) )
2001-08-20 17:45:43 +04:00
goto fail ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
_control - > write_proc = _line_splitter ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
pfd - > fn = _process_control ;
pfd - > minor = - 1 ;
_control - > data = pfd ;
2001-08-22 19:02:55 +04:00
return 0 ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
fail :
dm_fin_fs ( ) ;
2001-08-22 19:02:55 +04:00
return - ENOMEM ;
2001-08-20 12:03:02 +04:00
}
2001-08-20 17:45:43 +04:00
void dm_fin_fs ( void )
2001-08-20 12:03:02 +04:00
{
2001-08-20 17:45:43 +04:00
if ( _control ) {
remove_proc_entry ( _control_name , _proc_dir ) ;
_control = 0 ;
}
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
if ( _proc_dir ) {
remove_proc_entry ( _fs_dir , & proc_root ) ;
_proc_dir = 0 ;
}
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
if ( _dev_dir )
devfs_unregister ( _dev_dir ) ;
}
2001-08-20 12:03:02 +04:00
2001-08-22 19:01:09 +04:00
int dm_fs_add ( struct mapped_device * md )
{
struct pf_data * pfd = kmalloc ( sizeof ( * pfd ) , GFP_KERNEL ) ;
if ( ! pfd )
return - ENOMEM ;
pfd - > fn = _process_table ;
pfd - > minor = MINOR ( md - > dev ) ;
2001-08-23 00:10:06 +04:00
if ( ! ( md - > pde = create_proc_entry ( md - > name , S_IRUGO | S_IWUSR ,
_proc_dir ) ) ) {
2001-08-22 19:01:09 +04:00
kfree ( pfd ) ;
return - ENOMEM ;
}
md - > pde - > write_proc = _line_splitter ;
md - > pde - > data = pfd ;
2001-08-22 19:33:08 +04:00
md - > devfs_entry =
2001-08-23 00:10:06 +04:00
devfs_register ( _dev_dir , md - > name , DEVFS_FL_CURRENT_OWNER ,
2001-08-22 19:33:08 +04:00
MAJOR ( md - > dev ) , MINOR ( md - > dev ) ,
S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP ,
& dm_blk_dops , NULL ) ;
if ( ! md - > devfs_entry ) {
kfree ( pfd ) ;
remove_proc_entry ( md - > name , _proc_dir ) ;
md - > pde = 0 ;
return - ENOMEM ; /* FIXME: better error ? */
}
2001-08-22 19:01:09 +04:00
return 0 ;
}
int dm_fs_remove ( struct mapped_device * md )
{
if ( md - > pde ) {
kfree ( md - > pde - > data ) ;
remove_proc_entry ( md - > name , _proc_dir ) ;
2001-08-22 19:33:08 +04:00
md - > pde = 0 ;
2001-08-22 19:01:09 +04:00
}
2001-08-22 19:33:08 +04:00
devfs_unregister ( md - > devfs_entry ) ;
md - > devfs_entry = 0 ;
2001-08-22 19:01:09 +04:00
return 0 ;
}
2001-08-20 17:45:43 +04:00
int _process_control ( const char * b , const char * e , int minor )
2001-08-20 12:03:02 +04:00
{
2001-08-20 17:45:43 +04:00
const char * wb , * we ;
2001-08-21 19:24:02 +04:00
char name [ 64 ] ;
2001-08-20 17:45:43 +04:00
int create = 0 ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
/*
* create < name > [ minor ]
* remove < name >
*/
if ( ! _get_word ( b , e , & wb , & we ) )
return - EINVAL ;
b = we ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
if ( ! _tok_cmp ( " create " , wb , we ) )
create = 1 ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
else if ( _tok_cmp ( " remove " , wb , we ) )
return - EINVAL ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
if ( ! _get_word ( b , e , & wb , & we ) )
return - EINVAL ;
b = we ;
2001-08-20 12:03:02 +04:00
2001-08-21 19:24:02 +04:00
_tok_cpy ( name , sizeof ( name ) , wb , we ) ;
2001-08-20 12:03:02 +04:00
2001-08-22 19:01:09 +04:00
if ( ! create )
return dm_remove ( name ) ;
else {
2001-08-20 17:45:43 +04:00
if ( _get_word ( b , e , & wb , & we ) ) {
2001-08-21 19:24:02 +04:00
minor = simple_strtol ( wb , ( char * * ) & we , 10 ) ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
if ( we = = wb )
return - EINVAL ;
}
2001-08-21 19:24:02 +04:00
return dm_create ( name , minor ) ;
2001-08-20 12:03:02 +04:00
}
2001-08-20 17:45:43 +04:00
return - EINVAL ;
2001-08-20 12:03:02 +04:00
}
2001-08-20 17:45:43 +04:00
static int _process_table ( const char * b , const char * e , int minor )
2001-08-20 12:03:02 +04:00
{
2001-08-20 17:45:43 +04:00
const char * wb , * we ;
2001-08-21 19:24:02 +04:00
struct mapped_device * md = dm_find_minor ( minor ) ;
void * context ;
int ret ;
2001-08-20 12:03:02 +04:00
if ( ! md )
return - ENXIO ;
2001-08-20 17:45:43 +04:00
if ( ! _get_word ( b , e , & wb , & we ) )
return - EINVAL ;
if ( ! _tok_cmp ( " begin " , b , e ) ) {
/* suspend the device if it's active */
dm_suspend ( md ) ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
/* start loading a table */
dm_start_table ( md ) ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
} else if ( ! _tok_cmp ( " end " , b , e ) ) {
/* activate the device ... <evil chuckle> ... */
dm_complete_table ( md ) ;
dm_activate ( md ) ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
} else {
/* add the new entry */
int len = we - wb ;
char high_s [ 64 ] , * ptr ;
char target [ 64 ] ;
struct target * t ;
offset_t high ;
if ( len > sizeof ( high_s ) )
return 0 ;
strncpy ( high_s , wb , we - wb ) ;
high_s [ len ] = ' \0 ' ;
2001-08-21 19:24:02 +04:00
high = simple_strtol ( high_s , & ptr , 10 ) ;
2001-08-20 17:45:43 +04:00
if ( ptr = = high_s )
return 0 ;
b = we ;
if ( ! _get_word ( b , e , & wb , & we ) )
return 0 ;
len = we - wb ;
if ( len > sizeof ( target ) )
return 0 ;
strncpy ( target , wb , len ) ;
target [ len ] = ' \0 ' ;
if ( ! ( t = dm_get_target ( target ) ) )
return 0 ;
2001-08-21 19:24:02 +04:00
if ( ( ret = t - > ctr ( md - > highs [ md - > num_targets - 1 ] ,
high , md , we , & context ) ) )
return ret ;
dm_add_entry ( md , high , t - > map , context ) ;
2001-08-20 12:03:02 +04:00
}
2001-08-20 17:45:43 +04:00
return 1 ;
}
static const char * _eat_space ( const char * b , const char * e )
{
while ( b ! = e & & isspace ( ( int ) * b ) )
b + + ;
return b ;
}
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
static int _get_word ( const char * b , const char * e ,
2001-08-21 19:24:02 +04:00
const char * * wb , const char * * we )
2001-08-20 17:45:43 +04:00
{
b = _eat_space ( b , e ) ;
if ( b = = e )
return 0 ;
* wb = b ;
2001-08-22 18:13:26 +04:00
while ( b ! = e & & ! isspace ( ( int ) * b ) )
2001-08-20 17:45:43 +04:00
b + + ;
2001-08-22 17:52:26 +04:00
* we = b ;
2001-08-20 17:45:43 +04:00
return 1 ;
}
static int _line_splitter ( struct file * file , const char * buffer ,
2001-08-21 19:24:02 +04:00
unsigned long count , void * data )
2001-08-20 17:45:43 +04:00
{
2001-08-22 18:13:26 +04:00
int r ;
2001-08-20 17:45:43 +04:00
const char * b = buffer , * e = buffer + count , * lb ;
struct pf_data * pfd = ( struct pf_data * ) data ;
while ( b < e ) {
b = _eat_space ( b , e ) ;
if ( b = = e )
2001-08-22 18:30:30 +04:00
break ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
lb = b ;
while ( ( b ! = e ) & & * b ! = ' \n ' )
b + + ;
2001-08-22 18:13:26 +04:00
if ( ( r = pfd - > fn ( lb , b , pfd - > minor ) ) )
return r ;
2001-08-20 12:03:02 +04:00
}
2001-08-20 17:45:43 +04:00
return count ;
}
static int _tok_cmp ( const char * str , const char * b , const char * e )
{
while ( * str & & b ! = e ) {
if ( * str < * b )
return - 1 ;
if ( * str > * b )
return 1 ;
str + + , b + + ;
}
if ( ! * str & & b = = e )
return 0 ;
if ( * str )
return 1 ;
2001-08-20 12:03:02 +04:00
2001-08-20 17:45:43 +04:00
return - 1 ;
2001-08-20 12:03:02 +04:00
}
2001-08-21 19:24:02 +04:00
static void _tok_cpy ( char * dest , size_t max ,
const char * b , const char * e )
{
size_t len = e - b ;
if ( len > - - max )
len = max ;
strncpy ( dest , b , len ) ;
2001-08-22 18:30:30 +04:00
dest [ len ] = ' \0 ' ;
2001-08-21 19:24:02 +04:00
}