2005-04-17 02:20:36 +04:00
/*
* truncate . c
*
* PURPOSE
* Truncate handling routines for the OSTA - UDF ( tm ) filesystem .
*
* COPYRIGHT
* This file is distributed under the terms of the GNU General Public
* License ( GPL ) . Copies of the GPL can be obtained from :
* ftp : //prep.ai.mit.edu/pub/gnu/GPL
* Each contributing author retains all rights to their own work .
*
* ( C ) 1999 - 2004 Ben Fennema
* ( C ) 1999 Stelias Computing Inc
*
* HISTORY
*
* 02 / 24 / 99 blf Created .
*
*/
# include "udfdecl.h"
# include <linux/fs.h>
# include <linux/mm.h>
# include <linux/udf_fs.h>
# include <linux/buffer_head.h>
# include "udf_i.h"
# include "udf_sb.h"
static void extent_trunc ( struct inode * inode , kernel_lb_addr bloc , int extoffset ,
kernel_lb_addr eloc , int8_t etype , uint32_t elen , struct buffer_head * bh , uint32_t nelen )
{
kernel_lb_addr neloc = { 0 , 0 } ;
int last_block = ( elen + inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ;
int first_block = ( nelen + inode - > i_sb - > s_blocksize - 1 ) > > inode - > i_sb - > s_blocksize_bits ;
if ( nelen )
{
if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) )
{
udf_free_blocks ( inode - > i_sb , inode , eloc , 0 , last_block ) ;
etype = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) ;
}
else
neloc = eloc ;
nelen = ( etype < < 30 ) | nelen ;
}
if ( elen ! = nelen )
{
udf_write_aext ( inode , bloc , & extoffset , neloc , nelen , bh , 0 ) ;
if ( last_block - first_block > 0 )
{
if ( etype = = ( EXT_RECORDED_ALLOCATED > > 30 ) )
mark_inode_dirty ( inode ) ;
if ( etype ! = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) )
udf_free_blocks ( inode - > i_sb , inode , eloc , first_block , last_block - first_block ) ;
}
}
}
void udf_discard_prealloc ( struct inode * inode )
{
kernel_lb_addr bloc , eloc ;
uint32_t extoffset = 0 , elen , nelen ;
uint64_t lbcount = 0 ;
int8_t etype = - 1 , netype ;
struct buffer_head * bh = NULL ;
int adsize ;
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_IN_ICB | |
inode - > i_size = = UDF_I_LENEXTENTS ( inode ) )
{
return ;
}
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_SHORT )
adsize = sizeof ( short_ad ) ;
else if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_LONG )
adsize = sizeof ( long_ad ) ;
else
adsize = 0 ;
bloc = UDF_I_LOCATION ( inode ) ;
while ( ( netype = udf_next_aext ( inode , & bloc , & extoffset , & eloc , & elen , & bh , 1 ) ) ! = - 1 )
{
etype = netype ;
lbcount + = elen ;
if ( lbcount > inode - > i_size & & lbcount - inode - > i_size < inode - > i_sb - > s_blocksize )
{
nelen = elen - ( lbcount - inode - > i_size ) ;
extent_trunc ( inode , bloc , extoffset - adsize , eloc , etype , elen , bh , nelen ) ;
lbcount = inode - > i_size ;
}
}
if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) )
{
extoffset - = adsize ;
lbcount - = elen ;
extent_trunc ( inode , bloc , extoffset , eloc , etype , elen , bh , 0 ) ;
if ( ! bh )
{
UDF_I_LENALLOC ( inode ) = extoffset - udf_file_entry_alloc_offset ( inode ) ;
mark_inode_dirty ( inode ) ;
}
else
{
struct allocExtDesc * aed = ( struct allocExtDesc * ) ( bh - > b_data ) ;
aed - > lengthAllocDescs = cpu_to_le32 ( extoffset - sizeof ( struct allocExtDesc ) ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | | UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
udf_update_tag ( bh - > b_data , extoffset ) ;
else
udf_update_tag ( bh - > b_data , sizeof ( struct allocExtDesc ) ) ;
mark_buffer_dirty_inode ( bh , inode ) ;
}
}
UDF_I_LENEXTENTS ( inode ) = lbcount ;
udf_release_data ( bh ) ;
}
void udf_truncate_extents ( struct inode * inode )
{
kernel_lb_addr bloc , eloc , neloc = { 0 , 0 } ;
uint32_t extoffset , elen , offset , nelen = 0 , lelen = 0 , lenalloc ;
int8_t etype ;
int first_block = inode - > i_size > > inode - > i_sb - > s_blocksize_bits ;
struct buffer_head * bh = NULL ;
int adsize ;
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_SHORT )
adsize = sizeof ( short_ad ) ;
else if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_LONG )
adsize = sizeof ( long_ad ) ;
else
adsize = 0 ;
etype = inode_bmap ( inode , first_block , & bloc , & extoffset , & eloc , & elen , & offset , & bh ) ;
offset + = ( inode - > i_size & ( inode - > i_sb - > s_blocksize - 1 ) ) ;
if ( etype ! = - 1 )
{
extoffset - = adsize ;
extent_trunc ( inode , bloc , extoffset , eloc , etype , elen , bh , offset ) ;
extoffset + = adsize ;
if ( offset )
lenalloc = extoffset ;
else
lenalloc = extoffset - adsize ;
if ( ! bh )
lenalloc - = udf_file_entry_alloc_offset ( inode ) ;
else
lenalloc - = sizeof ( struct allocExtDesc ) ;
while ( ( etype = udf_current_aext ( inode , & bloc , & extoffset , & eloc , & elen , & bh , 0 ) ) ! = - 1 )
{
if ( etype = = ( EXT_NEXT_EXTENT_ALLOCDECS > > 30 ) )
{
udf_write_aext ( inode , bloc , & extoffset , neloc , nelen , bh , 0 ) ;
extoffset = 0 ;
if ( lelen )
{
if ( ! bh )
BUG ( ) ;
else
memset ( bh - > b_data , 0x00 , sizeof ( struct allocExtDesc ) ) ;
udf_free_blocks ( inode - > i_sb , inode , bloc , 0 , lelen ) ;
}
else
{
if ( ! bh )
{
UDF_I_LENALLOC ( inode ) = lenalloc ;
mark_inode_dirty ( inode ) ;
}
else
{
struct allocExtDesc * aed = ( struct allocExtDesc * ) ( bh - > b_data ) ;
aed - > lengthAllocDescs = cpu_to_le32 ( lenalloc ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | | UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
udf_update_tag ( bh - > b_data , lenalloc +
sizeof ( struct allocExtDesc ) ) ;
else
udf_update_tag ( bh - > b_data , sizeof ( struct allocExtDesc ) ) ;
mark_buffer_dirty_inode ( bh , inode ) ;
}
}
udf_release_data ( bh ) ;
extoffset = sizeof ( struct allocExtDesc ) ;
bloc = eloc ;
bh = udf_tread ( inode - > i_sb , udf_get_lb_pblock ( inode - > i_sb , bloc , 0 ) ) ;
if ( elen )
lelen = ( elen + inode - > i_sb - > s_blocksize - 1 ) > >
inode - > i_sb - > s_blocksize_bits ;
else
lelen = 1 ;
}
else
{
extent_trunc ( inode , bloc , extoffset , eloc , etype , elen , bh , 0 ) ;
extoffset + = adsize ;
}
}
if ( lelen )
{
if ( ! bh )
BUG ( ) ;
else
memset ( bh - > b_data , 0x00 , sizeof ( struct allocExtDesc ) ) ;
udf_free_blocks ( inode - > i_sb , inode , bloc , 0 , lelen ) ;
}
else
{
if ( ! bh )
{
UDF_I_LENALLOC ( inode ) = lenalloc ;
mark_inode_dirty ( inode ) ;
}
else
{
struct allocExtDesc * aed = ( struct allocExtDesc * ) ( bh - > b_data ) ;
aed - > lengthAllocDescs = cpu_to_le32 ( lenalloc ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | | UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
udf_update_tag ( bh - > b_data , lenalloc +
sizeof ( struct allocExtDesc ) ) ;
else
udf_update_tag ( bh - > b_data , sizeof ( struct allocExtDesc ) ) ;
mark_buffer_dirty_inode ( bh , inode ) ;
}
}
}
else if ( inode - > i_size )
{
if ( offset )
{
2006-08-15 15:56:26 +04:00
/*
* OK , there is not extent covering inode - > i_size and
* no extent above inode - > i_size = > truncate is
* extending the file by ' offset ' .
*/
if ( ( ! bh & & extoffset = = udf_file_entry_alloc_offset ( inode ) ) | |
( bh & & extoffset = = sizeof ( struct allocExtDesc ) ) ) {
/* File has no extents at all! */
memset ( & eloc , 0x00 , sizeof ( kernel_lb_addr ) ) ;
elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset ;
udf_add_aext ( inode , & bloc , & extoffset , eloc , elen , & bh , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-15 15:56:26 +04:00
else {
2005-04-17 02:20:36 +04:00
extoffset - = adsize ;
2006-08-15 15:56:26 +04:00
etype = udf_next_aext ( inode , & bloc , & extoffset , & eloc , & elen , & bh , 1 ) ;
if ( etype = = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) )
{
extoffset - = adsize ;
elen = EXT_NOT_RECORDED_NOT_ALLOCATED | ( elen + offset ) ;
udf_write_aext ( inode , bloc , & extoffset , eloc , elen , bh , 0 ) ;
}
else if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) )
2005-04-17 02:20:36 +04:00
{
2006-08-15 15:56:26 +04:00
kernel_lb_addr neloc = { 0 , 0 } ;
2005-04-17 02:20:36 +04:00
extoffset - = adsize ;
2006-08-15 15:56:26 +04:00
nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
( ( elen + offset + inode - > i_sb - > s_blocksize - 1 ) &
2005-04-17 02:20:36 +04:00
~ ( inode - > i_sb - > s_blocksize - 1 ) ) ;
2006-08-15 15:56:26 +04:00
udf_write_aext ( inode , bloc , & extoffset , neloc , nelen , bh , 1 ) ;
udf_add_aext ( inode , & bloc , & extoffset , eloc , ( etype < < 30 ) | elen , & bh , 1 ) ;
}
else
{
if ( elen & ( inode - > i_sb - > s_blocksize - 1 ) )
{
extoffset - = adsize ;
elen = EXT_RECORDED_ALLOCATED |
( ( elen + inode - > i_sb - > s_blocksize - 1 ) &
~ ( inode - > i_sb - > s_blocksize - 1 ) ) ;
udf_write_aext ( inode , bloc , & extoffset , eloc , elen , bh , 1 ) ;
}
memset ( & eloc , 0x00 , sizeof ( kernel_lb_addr ) ) ;
elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset ;
udf_add_aext ( inode , & bloc , & extoffset , eloc , elen , & bh , 1 ) ;
2005-04-17 02:20:36 +04:00
}
}
}
}
UDF_I_LENEXTENTS ( inode ) = inode - > i_size ;
udf_release_data ( bh ) ;
}