2001-09-18 19:38:54 +04:00
/*
* dmfs - table . 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 .
*/
# include <linux/config.h>
# include <linux/fs.h>
2001-10-15 20:40:17 +04:00
# include <linux/mm.h>
2001-09-21 02:58:06 +04:00
2001-10-11 01:49:21 +04:00
# include "dm.h"
2001-09-21 02:58:06 +04:00
2001-10-11 01:49:21 +04:00
static offset_t start_of_next_range ( struct dm_table * t )
{
offset_t n = 0 ;
if ( t - > num_targets ) {
n = t - > highs [ t - > num_targets - 1 ] + 1 ;
}
return n ;
}
2001-10-12 14:06:40 +04:00
static void dmfs_parse_line ( struct dm_table * t , unsigned num , char * str )
2001-09-21 02:58:06 +04:00
{
char * p = str ;
const char * tok ;
offset_t start , size , high ;
void * context ;
struct target_type * ttype ;
2001-10-11 01:49:21 +04:00
int rv = 0 ;
char * msg ;
2001-09-21 02:58:06 +04:00
2001-10-11 01:49:21 +04:00
msg = " No start argument " ;
2001-09-24 19:10:33 +04:00
tok = next_token ( & p ) ;
2001-09-21 02:58:06 +04:00
if ( ! tok )
2001-10-11 01:49:21 +04:00
goto out ;
2001-09-21 02:58:06 +04:00
start = simple_strtoul ( tok , NULL , 10 ) ;
2001-10-11 01:49:21 +04:00
msg = " No size argument " ;
2001-09-24 19:10:33 +04:00
tok = next_token ( & p ) ;
2001-09-21 02:58:06 +04:00
if ( ! tok )
2001-10-11 01:49:21 +04:00
goto out ;
2001-09-21 02:58:06 +04:00
size = simple_strtoul ( tok , NULL , 10 ) ;
2001-10-11 01:49:21 +04:00
msg = " Gap in table " ;
if ( start ! = start_of_next_range ( t ) )
goto out ;
msg = " No target type " ;
2001-09-24 19:10:33 +04:00
tok = next_token ( & p ) ;
2001-09-21 02:58:06 +04:00
if ( ! tok )
2001-10-11 01:49:21 +04:00
goto out ;
2001-09-21 02:58:06 +04:00
2001-10-11 01:49:21 +04:00
msg = " Target type unknown " ;
2001-09-21 02:58:06 +04:00
ttype = dm_get_target_type ( tok ) ;
if ( ttype ) {
2001-10-11 01:49:21 +04:00
msg = " This message should never appear (constructor error) " ;
rv = ttype - > ctr ( t , start , size , p , & context ) ;
2001-10-12 00:29:00 +04:00
msg = context ;
2001-10-12 14:06:40 +04:00
if ( rv = = 0 ) {
2001-10-12 00:29:00 +04:00
printk ( KERN_DEBUG " %ul %ul %s %s " , start , size ,
ttype - > name ,
ttype - > print ( context ) ) ;
2001-10-11 01:49:21 +04:00
msg = " Error adding target to table " ;
2001-09-21 02:58:06 +04:00
high = start + ( size - 1 ) ;
if ( dm_table_add_target ( t , high , ttype , context ) = = 0 )
2001-10-11 01:49:21 +04:00
return ;
2001-09-21 02:58:06 +04:00
ttype - > dtr ( t , context ) ;
}
dm_put_target_type ( ttype ) ;
}
2001-10-11 01:49:21 +04:00
out :
dmfs_add_error ( t , num , msg ) ;
2001-09-21 02:58:06 +04:00
}
2001-10-11 01:49:21 +04:00
2001-09-21 02:58:06 +04:00
static int dmfs_copy ( char * dst , int dstlen , char * src , int srclen , int * flag )
{
int copied = 0 ;
while ( dstlen & & srclen ) {
* dst = * src + + ;
copied + + ;
if ( * dst = = ' \n ' )
goto end_of_line ;
dst + + ;
dstlen - - ;
srclen - - ;
}
out :
return copied ;
end_of_line :
* flag = 1 ;
* dst = 0 ;
goto out ;
}
2001-10-11 01:49:21 +04:00
static int dmfs_parse_page ( struct dm_table * t , char * buf , int len , char * tmp , unsigned long * tmpl , int * num )
2001-09-21 02:58:06 +04:00
{
int copied ;
do {
int flag = 0 ;
copied = dmfs_copy ( tmp + * tmpl , PAGE_SIZE - * tmpl - 1 , buf , len , & flag ) ;
buf + = copied ;
len - = copied ;
if ( * tmpl + copied = = PAGE_SIZE - 1 )
2001-10-11 01:49:21 +04:00
goto line_too_long ;
2001-09-21 02:58:06 +04:00
* tmpl = copied ;
if ( flag ) {
2001-10-11 01:49:21 +04:00
dmfs_parse_line ( t , * num , tmp ) ;
( * num ) + + ;
2001-09-21 02:58:06 +04:00
* tmpl = 0 ;
}
} while ( len > 0 ) ;
return 0 ;
2001-10-11 01:49:21 +04:00
line_too_long :
dmfs_add_error ( t , * num , " Line too long " ) ;
/* FIXME: Add code to recover from this */
return - 1 ;
2001-09-21 02:58:06 +04:00
}
static struct dm_table * dmfs_parse ( struct inode * inode )
{
struct address_space * mapping = inode - > i_mapping ;
unsigned long index = 0 ;
unsigned long end_index , end_offset ;
unsigned long page ;
unsigned long rem = 0 ;
struct dm_table * t ;
2001-10-15 20:40:17 +04:00
struct page * pg ;
2001-10-11 01:49:21 +04:00
int num = 0 ;
2001-09-21 02:58:06 +04:00
if ( inode - > i_size = = 0 )
return NULL ;
page = __get_free_page ( GFP_KERNEL ) ;
if ( ! page )
return NULL ;
2001-10-15 20:40:17 +04:00
t = dm_table_create ( ) ;
2001-09-21 02:58:06 +04:00
if ( ! t ) {
free_page ( page ) ;
return NULL ;
}
2001-10-11 01:49:21 +04:00
end_index = inode - > i_size > > PAGE_CACHE_SHIFT ;
end_offset = inode - > i_size & ( PAGE_CACHE_SIZE - 1 ) ;
2001-09-21 02:58:06 +04:00
do {
unsigned long end = ( index = = end_index ) ? end_offset : PAGE_CACHE_SIZE ;
2001-10-15 20:40:17 +04:00
pg = find_get_page ( mapping , index ) ;
2001-09-21 02:58:06 +04:00
if ( pg ) {
2001-10-11 01:49:21 +04:00
char * kaddr ;
int rv ;
2001-09-21 02:58:06 +04:00
if ( ! Page_Uptodate ( pg ) )
goto broken ;
2001-10-11 01:49:21 +04:00
kaddr = kmap ( pg ) ;
2001-10-12 14:06:40 +04:00
rv = dmfs_parse_page ( t , kaddr , end , ( char * ) page , & rem , & num ) ;
2001-10-11 01:49:21 +04:00
kunmap ( pg ) ;
if ( rv )
2001-09-21 02:58:06 +04:00
goto parse_error ;
}
page_cache_release ( pg ) ;
index + + ;
} while ( index ! = end_index ) ;
free_page ( page ) ;
if ( dm_table_complete ( t ) = = 0 )
return t ;
2001-10-15 20:40:17 +04:00
dm_table_destroy ( t ) ;
2001-09-21 02:58:06 +04:00
return NULL ;
broken :
printk ( KERN_ERR " dmfs_parse: Page not uptodate \n " ) ;
free_page ( page ) ;
2001-10-15 20:40:17 +04:00
dm_table_destroy ( t ) ;
2001-09-21 02:58:06 +04:00
return NULL ;
parse_error :
printk ( KERN_ERR " dmfs_parse: Parse error \n " ) ;
free_page ( page ) ;
2001-10-15 20:40:17 +04:00
dm_table_destroy ( t ) ;
2001-09-21 02:58:06 +04:00
return NULL ;
}
2001-09-18 19:38:54 +04:00
2001-10-12 14:06:40 +04:00
static int dmfs_table_release ( struct inode * inode , struct file * f )
2001-09-18 19:38:54 +04:00
{
2001-10-12 14:06:40 +04:00
struct dentry * dentry = f - > f_dentry ;
struct inode * parent = dentry - > d_parent - > d_inode ;
struct dmfs_i * dmi = DMFS_I ( parent ) ;
2001-09-20 01:27:46 +04:00
struct dm_table * table ;
2001-10-12 14:06:40 +04:00
if ( f - > f_mode & FMODE_WRITE ) {
2001-09-19 14:32:51 +04:00
2001-10-12 14:06:40 +04:00
down ( & dmi - > sem ) ;
table = dmfs_parse ( inode ) ;
if ( table ) {
if ( dmi - > table )
2001-10-15 20:40:17 +04:00
dm_table_destroy ( dmi - > table ) ;
2001-10-12 14:06:40 +04:00
dmi - > table = table ;
}
up ( & dmi - > sem ) ;
2001-09-21 02:58:06 +04:00
2001-10-12 14:06:40 +04:00
put_write_access ( parent ) ;
2001-09-21 02:58:06 +04:00
}
return 0 ;
2001-09-18 19:38:54 +04:00
}
2001-09-20 01:27:46 +04:00
static int dmfs_readpage ( struct file * file , struct page * page )
2001-09-18 19:38:54 +04:00
{
2001-10-15 20:40:17 +04:00
if ( ! Page_Uptodate ( page ) ) {
2001-09-20 01:27:46 +04:00
memset ( kmap ( page ) , 0 , PAGE_CACHE_SIZE ) ;
kunmap ( page ) ;
flush_dcache_page ( page ) ;
SetPageUptodate ( page ) ;
}
UnlockPage ( page ) ;
return 0 ;
2001-09-18 19:38:54 +04:00
}
2001-09-20 01:27:46 +04:00
static int dmfs_writepage ( struct page * page )
2001-09-18 19:38:54 +04:00
{
2001-09-20 01:27:46 +04:00
SetPageDirty ( page ) ;
UnlockPage ( page ) ;
return 0 ;
}
2001-09-18 19:38:54 +04:00
2001-09-20 01:27:46 +04:00
static int dmfs_prepare_write ( struct file * file , struct page * page ,
unsigned offset , unsigned to )
{
void * addr = kmap ( page ) ;
if ( ! Page_Uptodate ( page ) ) {
memset ( addr , 0 , PAGE_CACHE_SIZE ) ;
flush_dcache_page ( page ) ;
SetPageUptodate ( page ) ;
}
SetPageDirty ( page ) ;
return 0 ;
}
2001-09-19 14:32:51 +04:00
2001-09-20 01:27:46 +04:00
static int dmfs_commit_write ( struct file * file , struct page * page ,
unsigned offset , unsigned to )
{
struct inode * inode = page - > mapping - > host ;
loff_t pos = ( ( loff_t ) page - > index < < PAGE_CACHE_SHIFT ) + to ;
2001-09-18 19:38:54 +04:00
2001-09-20 01:27:46 +04:00
kunmap ( page ) ;
if ( pos > inode - > i_size )
inode - > i_size = pos ;
2001-09-18 19:38:54 +04:00
return 0 ;
}
2001-10-12 00:29:00 +04:00
/*
* There is a small race here in that two processes might call this at
* the same time and both fail . So its a fail safe race : - ) This should
* move into namei . c ( and thus use the spinlock and do this properly )
* at some stage if we continue to use this set of functions for ensuring
* exclusive write access to the file
*/
static int get_exclusive_write_access ( struct inode * inode )
{
if ( get_write_access ( inode ) )
return - 1 ;
if ( atomic_read ( & inode - > i_writecount ) ! = 1 ) {
put_write_access ( inode ) ;
return - 1 ;
}
return 0 ;
}
2001-09-27 00:24:39 +04:00
static int dmfs_table_open ( struct inode * inode , struct file * file )
{
struct dentry * dentry = file - > f_dentry ;
2001-10-12 14:06:40 +04:00
struct inode * parent = dentry - > d_parent - > d_inode ;
2001-09-27 00:24:39 +04:00
if ( file - > f_mode & FMODE_WRITE ) {
2001-10-12 14:06:40 +04:00
if ( get_exclusive_write_access ( parent ) )
2001-09-27 00:24:39 +04:00
return - EPERM ;
}
return 0 ;
}
2001-09-18 19:38:54 +04:00
static int dmfs_table_sync ( struct file * file , struct dentry * dentry , int datasync )
{
return 0 ;
}
2001-10-12 14:06:40 +04:00
struct address_space_operations dmfs_address_space_operations = {
2001-09-20 01:27:46 +04:00
readpage : dmfs_readpage ,
writepage : dmfs_writepage ,
prepare_write : dmfs_prepare_write ,
commit_write : dmfs_commit_write ,
} ;
2001-10-12 14:06:40 +04:00
static struct file_operations dmfs_table_file_operations = {
2001-09-20 01:27:46 +04:00
llseek : generic_file_llseek ,
read : generic_file_read ,
2001-10-11 01:49:21 +04:00
write : generic_file_write , /* FIXME: Needs to hold dmi->sem */
2001-09-27 00:24:39 +04:00
open : dmfs_table_open ,
release : dmfs_table_release ,
2001-09-18 19:38:54 +04:00
fsync : dmfs_table_sync ,
} ;
2001-10-12 14:06:40 +04:00
static struct inode_operations dmfs_table_inode_operations = {
2001-09-18 19:38:54 +04:00
} ;
2001-09-20 01:27:46 +04:00
int dmfs_create_table ( struct inode * dir , int mode )
2001-09-18 19:38:54 +04:00
{
struct inode * inode = new_inode ( dir - > i_sb ) ;
if ( inode ) {
inode - > i_mode = mode | S_IFREG ;
inode - > i_uid = current - > fsuid ;
inode - > i_gid = current - > fsgid ;
inode - > i_blksize = PAGE_CACHE_SIZE ;
inode - > i_blocks = 0 ;
inode - > i_rdev = NODEV ;
inode - > i_atime = inode - > i_ctime = inode - > i_mtime = CURRENT_TIME ;
2001-09-26 15:47:02 +04:00
inode - > i_mapping - > a_ops = & dmfs_address_space_operations ;
2001-09-18 19:38:54 +04:00
inode - > i_fop = & dmfs_table_file_operations ;
inode - > i_op = & dmfs_table_inode_operations ;
2001-10-11 01:49:21 +04:00
inode - > i_mapping = dir - > i_mapping ;
2001-09-18 19:38:54 +04:00
}
return inode ;
}