2005-04-17 02:20:36 +04:00
/*
* fs / bfs / file . c
* BFS file operations .
* Copyright ( C ) 1999 , 2000 Tigran Aivazian < tigran @ veritas . com >
*/
# include <linux/fs.h>
# include <linux/buffer_head.h>
# include <linux/smp_lock.h>
# include "bfs.h"
# undef DEBUG
# ifdef DEBUG
# define dprintf(x...) printf(x)
# else
# define dprintf(x...)
# endif
2006-03-28 13:56:42 +04:00
const struct file_operations bfs_file_operations = {
2005-04-17 02:20:36 +04:00
. llseek = generic_file_llseek ,
. read = generic_file_read ,
. write = generic_file_write ,
. mmap = generic_file_mmap ,
. sendfile = generic_file_sendfile ,
} ;
static int bfs_move_block ( unsigned long from , unsigned long to , struct super_block * sb )
{
struct buffer_head * bh , * new ;
bh = sb_bread ( sb , from ) ;
if ( ! bh )
return - EIO ;
new = sb_getblk ( sb , to ) ;
memcpy ( new - > b_data , bh - > b_data , bh - > b_size ) ;
mark_buffer_dirty ( new ) ;
bforget ( bh ) ;
brelse ( new ) ;
return 0 ;
}
2005-09-10 00:02:04 +04:00
static int bfs_move_blocks ( struct super_block * sb , unsigned long start ,
unsigned long end , unsigned long where )
2005-04-17 02:20:36 +04:00
{
unsigned long i ;
dprintf ( " %08lx-%08lx->%08lx \n " , start , end , where ) ;
for ( i = start ; i < = end ; i + + )
if ( bfs_move_block ( i , where + i , sb ) ) {
dprintf ( " failed to move block %08lx -> %08lx \n " , i , where + i ) ;
return - EIO ;
}
return 0 ;
}
static int bfs_get_block ( struct inode * inode , sector_t block ,
struct buffer_head * bh_result , int create )
{
2005-09-10 00:02:04 +04:00
unsigned long phys ;
2005-04-17 02:20:36 +04:00
int err ;
struct super_block * sb = inode - > i_sb ;
struct bfs_sb_info * info = BFS_SB ( sb ) ;
struct bfs_inode_info * bi = BFS_I ( inode ) ;
struct buffer_head * sbh = info - > si_sbh ;
2005-09-10 00:02:04 +04:00
if ( block > info - > si_blocks )
2005-04-17 02:20:36 +04:00
return - EIO ;
phys = bi - > i_sblock + block ;
if ( ! create ) {
if ( phys < = bi - > i_eblock ) {
2005-09-10 00:02:04 +04:00
dprintf ( " c=%d, b=%08lx, phys=%09lx (granted) \n " ,
create , ( unsigned long ) block , phys ) ;
2005-04-17 02:20:36 +04:00
map_bh ( bh_result , sb , phys ) ;
}
return 0 ;
}
/* if the file is not empty and the requested block is within the range
of blocks allocated for this file , we can grant it */
if ( inode - > i_size & & phys < = bi - > i_eblock ) {
dprintf ( " c=%d, b=%08lx, phys=%08lx (interim block granted) \n " ,
2005-09-10 00:02:04 +04:00
create , ( unsigned long ) block , phys ) ;
2005-04-17 02:20:36 +04:00
map_bh ( bh_result , sb , phys ) ;
return 0 ;
}
/* the rest has to be protected against itself */
lock_kernel ( ) ;
2005-09-10 00:02:04 +04:00
/* if the last data block for this file is the last allocated
block , we can extend the file trivially , without moving it
anywhere */
2005-04-17 02:20:36 +04:00
if ( bi - > i_eblock = = info - > si_lf_eblk ) {
dprintf ( " c=%d, b=%08lx, phys=%08lx (simple extension) \n " ,
2005-09-10 00:02:04 +04:00
create , ( unsigned long ) block , phys ) ;
2005-04-17 02:20:36 +04:00
map_bh ( bh_result , sb , phys ) ;
info - > si_freeb - = phys - bi - > i_eblock ;
info - > si_lf_eblk = bi - > i_eblock = phys ;
mark_inode_dirty ( inode ) ;
mark_buffer_dirty ( sbh ) ;
err = 0 ;
goto out ;
}
/* Ok, we have to move this entire file to the next free block */
phys = info - > si_lf_eblk + 1 ;
if ( bi - > i_sblock ) { /* if data starts on block 0 then there is no data */
err = bfs_move_blocks ( inode - > i_sb , bi - > i_sblock ,
bi - > i_eblock , phys ) ;
if ( err ) {
dprintf ( " failed to move ino=%08lx -> fs corruption \n " , inode - > i_ino ) ;
goto out ;
}
} else
err = 0 ;
2005-09-10 00:02:04 +04:00
dprintf ( " c=%d, b=%08lx, phys=%08lx (moved) \n " ,
create , ( unsigned long ) block , phys ) ;
2005-04-17 02:20:36 +04:00
bi - > i_sblock = phys ;
phys + = block ;
info - > si_lf_eblk = bi - > i_eblock = phys ;
/* this assumes nothing can write the inode back while we are here
* and thus update inode - > i_blocks ! ( XXX ) */
info - > si_freeb - = bi - > i_eblock - bi - > i_sblock + 1 - inode - > i_blocks ;
mark_inode_dirty ( inode ) ;
mark_buffer_dirty ( sbh ) ;
map_bh ( bh_result , sb , phys ) ;
out :
unlock_kernel ( ) ;
return err ;
}
static int bfs_writepage ( struct page * page , struct writeback_control * wbc )
{
return block_write_full_page ( page , bfs_get_block , wbc ) ;
}
static int bfs_readpage ( struct file * file , struct page * page )
{
return block_read_full_page ( page , bfs_get_block ) ;
}
static int bfs_prepare_write ( struct file * file , struct page * page , unsigned from , unsigned to )
{
return block_prepare_write ( page , from , to , bfs_get_block ) ;
}
static sector_t bfs_bmap ( struct address_space * mapping , sector_t block )
{
return generic_block_bmap ( mapping , block , bfs_get_block ) ;
}
struct address_space_operations bfs_aops = {
. readpage = bfs_readpage ,
. writepage = bfs_writepage ,
. sync_page = block_sync_page ,
. prepare_write = bfs_prepare_write ,
. commit_write = generic_commit_write ,
. bmap = bfs_bmap ,
} ;
struct inode_operations bfs_file_inops ;