2005-04-16 15:20:36 -07: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"
2007-07-19 01:47:43 -07:00
static void extent_trunc ( struct inode * inode , struct extent_position * epos ,
kernel_lb_addr eloc , int8_t etype , uint32_t elen ,
uint32_t nelen )
2005-04-16 15:20:36 -07:00
{
2007-07-21 04:37:18 -07:00
kernel_lb_addr neloc = { } ;
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 ;
2005-04-16 15:20:36 -07:00
2007-07-19 01:47:43 -07:00
if ( nelen ) {
if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) ) {
udf_free_blocks ( inode - > i_sb , inode , eloc , 0 ,
last_block ) ;
2005-04-16 15:20:36 -07:00
etype = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) ;
2007-07-19 01:47:43 -07:00
} else
2005-04-16 15:20:36 -07:00
neloc = eloc ;
nelen = ( etype < < 30 ) | nelen ;
}
2007-07-19 01:47:43 -07:00
if ( elen ! = nelen ) {
2007-05-08 00:35:14 -07:00
udf_write_aext ( inode , epos , neloc , nelen , 0 ) ;
2007-07-19 01:47:43 -07:00
if ( last_block - first_block > 0 ) {
2005-04-16 15:20:36 -07:00
if ( etype = = ( EXT_RECORDED_ALLOCATED > > 30 ) )
mark_inode_dirty ( inode ) ;
if ( etype ! = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) )
2007-07-19 01:47:43 -07:00
udf_free_blocks ( inode - > i_sb , inode , eloc ,
first_block ,
last_block - first_block ) ;
2005-04-16 15:20:36 -07:00
}
}
}
2007-06-16 10:16:14 -07:00
/*
* Truncate the last extent to match i_size . This function assumes
* that preallocation extent is already truncated .
*/
void udf_truncate_tail_extent ( struct inode * inode )
2005-04-16 15:20:36 -07:00
{
2007-07-21 04:37:18 -07:00
struct extent_position epos = { } ;
2007-05-08 00:35:14 -07:00
kernel_lb_addr eloc ;
uint32_t elen , nelen ;
2005-04-16 15:20:36 -07:00
uint64_t lbcount = 0 ;
int8_t etype = - 1 , netype ;
int adsize ;
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_IN_ICB | |
2007-06-16 10:16:14 -07:00
inode - > i_size = = UDF_I_LENEXTENTS ( inode ) )
return ;
/* Are we going to delete the file anyway? */
if ( inode - > i_nlink = = 0 )
2005-04-16 15:20:36 -07:00
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
2007-06-16 10:16:14 -07:00
BUG ( ) ;
2005-04-16 15:20:36 -07:00
2007-05-08 00:35:14 -07:00
/* Find the last extent in the file */
2007-07-19 01:47:43 -07:00
while ( ( netype = udf_next_aext ( inode , & epos , & eloc , & elen , 1 ) ) ! = - 1 ) {
2005-04-16 15:20:36 -07:00
etype = netype ;
lbcount + = elen ;
2007-06-16 10:16:14 -07:00
if ( lbcount > inode - > i_size ) {
if ( lbcount - inode - > i_size > = inode - > i_sb - > s_blocksize )
printk ( KERN_WARNING
" udf_truncate_tail_extent(): Too long "
" extent after EOF in inode %u: i_size: "
" %Ld lbcount: %Ld extent %u+%u \n " ,
( unsigned ) inode - > i_ino ,
( long long ) inode - > i_size ,
( long long ) lbcount ,
( unsigned ) eloc . logicalBlockNum ,
( unsigned ) elen ) ;
2005-04-16 15:20:36 -07:00
nelen = elen - ( lbcount - inode - > i_size ) ;
2007-05-08 00:35:14 -07:00
epos . offset - = adsize ;
extent_trunc ( inode , & epos , eloc , etype , elen , nelen ) ;
epos . offset + = adsize ;
2007-06-16 10:16:14 -07:00
if ( udf_next_aext ( inode , & epos , & eloc , & elen , 1 ) ! = - 1 )
printk ( KERN_ERR " udf_truncate_tail_extent(): "
" Extent after EOF in inode %u. \n " ,
( unsigned ) inode - > i_ino ) ;
break ;
2005-04-16 15:20:36 -07:00
}
}
2007-06-16 10:16:14 -07:00
/* This inode entry is in-memory only and thus we don't have to mark
* the inode dirty */
UDF_I_LENEXTENTS ( inode ) = inode - > i_size ;
brelse ( epos . bh ) ;
}
void udf_discard_prealloc ( struct inode * inode )
{
2007-07-19 01:47:43 -07:00
struct extent_position epos = { NULL , 0 , { 0 , 0 } } ;
2007-06-16 10:16:14 -07:00
kernel_lb_addr eloc ;
uint32_t elen ;
uint64_t lbcount = 0 ;
int8_t etype = - 1 , netype ;
int adsize ;
if ( UDF_I_ALLOCTYPE ( inode ) = = ICBTAG_FLAG_AD_IN_ICB | |
2007-07-19 01:47:43 -07:00
inode - > i_size = = UDF_I_LENEXTENTS ( inode ) )
2007-06-16 10:16:14 -07:00
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 ;
epos . block = UDF_I_LOCATION ( inode ) ;
/* Find the last extent in the file */
while ( ( netype = udf_next_aext ( inode , & epos , & eloc , & elen , 1 ) ) ! = - 1 ) {
etype = netype ;
lbcount + = elen ;
}
2007-05-08 00:35:14 -07:00
if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) ) {
epos . offset - = adsize ;
2005-04-16 15:20:36 -07:00
lbcount - = elen ;
2007-05-08 00:35:14 -07:00
extent_trunc ( inode , & epos , eloc , etype , elen , 0 ) ;
2007-06-16 10:16:14 -07:00
if ( ! epos . bh ) {
2007-07-19 01:47:43 -07:00
UDF_I_LENALLOC ( inode ) =
2007-07-21 04:37:18 -07:00
epos . offset - udf_file_entry_alloc_offset ( inode ) ;
2005-04-16 15:20:36 -07:00
mark_inode_dirty ( inode ) ;
2007-06-16 10:16:14 -07:00
} else {
2007-07-19 01:47:43 -07:00
struct allocExtDesc * aed =
2007-07-21 04:37:18 -07:00
( struct allocExtDesc * ) ( epos . bh - > b_data ) ;
2007-07-19 01:47:43 -07:00
aed - > lengthAllocDescs =
2007-07-21 04:37:18 -07:00
cpu_to_le32 ( epos . offset -
sizeof ( struct allocExtDesc ) ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | |
UDF_SB_UDFREV ( inode - > i_sb ) > = 0x0201 )
2007-05-08 00:35:14 -07:00
udf_update_tag ( epos . bh - > b_data , epos . offset ) ;
2005-04-16 15:20:36 -07:00
else
2007-07-19 01:47:43 -07:00
udf_update_tag ( epos . bh - > b_data ,
sizeof ( struct allocExtDesc ) ) ;
2007-05-08 00:35:14 -07:00
mark_buffer_dirty_inode ( epos . bh , inode ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-06-16 10:16:14 -07:00
/* This inode entry is in-memory only and thus we don't have to mark
* the inode dirty */
2005-04-16 15:20:36 -07:00
UDF_I_LENEXTENTS ( inode ) = lbcount ;
2007-05-08 00:35:16 -07:00
brelse ( epos . bh ) ;
2005-04-16 15:20:36 -07:00
}
2007-07-19 01:47:43 -07:00
void udf_truncate_extents ( struct inode * inode )
2005-04-16 15:20:36 -07:00
{
2007-05-08 00:35:14 -07:00
struct extent_position epos ;
2007-07-21 04:37:18 -07:00
kernel_lb_addr eloc , neloc = { } ;
2007-05-08 00:35:14 -07:00
uint32_t elen , nelen = 0 , indirect_ext_len = 0 , lenalloc ;
2005-04-16 15:20:36 -07:00
int8_t etype ;
2007-05-08 00:35:21 -07:00
struct super_block * sb = inode - > i_sb ;
sector_t first_block = inode - > i_size > > sb - > s_blocksize_bits , offset ;
2007-05-08 00:35:13 -07:00
loff_t byte_offset ;
2005-04-16 15:20:36 -07:00
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
2007-05-08 00:35:14 -07:00
BUG ( ) ;
2005-04-16 15:20:36 -07:00
2007-05-08 00:35:14 -07:00
etype = inode_bmap ( inode , first_block , & epos , & eloc , & elen , & offset ) ;
2007-07-21 04:37:18 -07:00
byte_offset = ( offset < < sb - > s_blocksize_bits ) +
( inode - > i_size & ( sb - > s_blocksize - 1 ) ) ;
2007-07-19 01:47:43 -07:00
if ( etype ! = - 1 ) {
2007-05-08 00:35:14 -07:00
epos . offset - = adsize ;
extent_trunc ( inode , & epos , eloc , etype , elen , byte_offset ) ;
epos . offset + = adsize ;
2007-05-08 00:35:13 -07:00
if ( byte_offset )
2007-05-08 00:35:14 -07:00
lenalloc = epos . offset ;
2005-04-16 15:20:36 -07:00
else
2007-05-08 00:35:14 -07:00
lenalloc = epos . offset - adsize ;
2005-04-16 15:20:36 -07:00
2007-05-08 00:35:14 -07:00
if ( ! epos . bh )
2005-04-16 15:20:36 -07:00
lenalloc - = udf_file_entry_alloc_offset ( inode ) ;
else
lenalloc - = sizeof ( struct allocExtDesc ) ;
2007-07-21 04:37:18 -07:00
while ( ( etype = udf_current_aext ( inode , & epos , & eloc , & elen , 0 ) ) ! = - 1 ) {
2007-07-19 01:47:43 -07:00
if ( etype = = ( EXT_NEXT_EXTENT_ALLOCDECS > > 30 ) ) {
2007-05-08 00:35:14 -07:00
udf_write_aext ( inode , & epos , neloc , nelen , 0 ) ;
2007-07-19 01:47:43 -07:00
if ( indirect_ext_len ) {
2007-05-08 00:35:14 -07:00
/* We managed to free all extents in the
* indirect extent - free it too */
if ( ! epos . bh )
2005-04-16 15:20:36 -07:00
BUG ( ) ;
2007-07-19 01:47:43 -07:00
udf_free_blocks ( sb , inode , epos . block ,
0 , indirect_ext_len ) ;
} else {
if ( ! epos . bh ) {
2007-07-21 04:37:18 -07:00
UDF_I_LENALLOC ( inode ) = lenalloc ;
2005-04-16 15:20:36 -07:00
mark_inode_dirty ( inode ) ;
2007-07-19 01:47:43 -07:00
} else {
struct allocExtDesc * aed =
2007-07-21 04:37:18 -07:00
( struct allocExtDesc * ) ( epos . bh - > b_data ) ;
2007-07-19 01:47:43 -07:00
aed - > lengthAllocDescs =
cpu_to_le32 ( lenalloc ) ;
2007-07-21 04:37:18 -07:00
if ( ! UDF_QUERY_FLAG ( sb , UDF_FLAG_STRICT ) | |
UDF_SB_UDFREV ( sb ) > = 0x0201 )
udf_update_tag ( epos . bh - > b_data ,
lenalloc +
sizeof ( struct allocExtDesc ) ) ;
2005-04-16 15:20:36 -07:00
else
2007-07-21 04:37:18 -07:00
udf_update_tag ( epos . bh - > b_data ,
sizeof ( struct allocExtDesc ) ) ;
mark_buffer_dirty_inode ( epos . bh , inode ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-05-08 00:35:14 -07:00
brelse ( epos . bh ) ;
epos . offset = sizeof ( struct allocExtDesc ) ;
epos . block = eloc ;
2007-07-21 04:37:18 -07:00
epos . bh = udf_tread ( sb , udf_get_lb_pblock ( sb , eloc , 0 ) ) ;
2005-04-16 15:20:36 -07:00
if ( elen )
2007-07-21 04:37:18 -07:00
indirect_ext_len = ( elen + sb - > s_blocksize - 1 ) > >
sb - > s_blocksize_bits ;
2005-04-16 15:20:36 -07:00
else
2007-05-08 00:35:14 -07:00
indirect_ext_len = 1 ;
2007-07-19 01:47:43 -07:00
} else {
2007-07-21 04:37:18 -07:00
extent_trunc ( inode , & epos , eloc , etype , elen , 0 ) ;
2007-05-08 00:35:14 -07:00
epos . offset + = adsize ;
2005-04-16 15:20:36 -07:00
}
}
2007-07-19 01:47:43 -07:00
if ( indirect_ext_len ) {
2007-05-08 00:35:14 -07:00
if ( ! epos . bh )
2005-04-16 15:20:36 -07:00
BUG ( ) ;
2007-07-19 01:47:43 -07:00
udf_free_blocks ( sb , inode , epos . block , 0 ,
indirect_ext_len ) ;
} else {
if ( ! epos . bh ) {
2005-04-16 15:20:36 -07:00
UDF_I_LENALLOC ( inode ) = lenalloc ;
mark_inode_dirty ( inode ) ;
2007-07-19 01:47:43 -07:00
} else {
struct allocExtDesc * aed =
( struct allocExtDesc * ) ( epos . bh - > b_data ) ;
2005-04-16 15:20:36 -07:00
aed - > lengthAllocDescs = cpu_to_le32 ( lenalloc ) ;
2007-07-21 04:37:18 -07:00
if ( ! UDF_QUERY_FLAG ( sb , UDF_FLAG_STRICT ) | |
UDF_SB_UDFREV ( sb ) > = 0x0201 )
2007-07-19 01:47:43 -07:00
udf_update_tag ( epos . bh - > b_data ,
2007-07-21 04:37:18 -07:00
lenalloc + sizeof ( struct allocExtDesc ) ) ;
2005-04-16 15:20:36 -07:00
else
2007-07-19 01:47:43 -07:00
udf_update_tag ( epos . bh - > b_data ,
2007-07-21 04:37:18 -07:00
sizeof ( struct allocExtDesc ) ) ;
2007-05-08 00:35:14 -07:00
mark_buffer_dirty_inode ( epos . bh , inode ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-07-19 01:47:43 -07:00
} else if ( inode - > i_size ) {
if ( byte_offset ) {
2007-05-08 00:35:21 -07:00
kernel_long_ad extent ;
2006-08-15 13:56:26 +02:00
/*
* OK , there is not extent covering inode - > i_size and
* no extent above inode - > i_size = > truncate is
2007-05-08 00:35:21 -07:00
* extending the file by ' offset ' blocks .
2006-08-15 13:56:26 +02:00
*/
2007-07-21 04:37:18 -07:00
if ( ( ! epos . bh & &
epos . offset = = udf_file_entry_alloc_offset ( inode ) ) | |
( epos . bh & & epos . offset = = sizeof ( struct allocExtDesc ) ) ) {
2007-05-08 00:35:21 -07:00
/* File has no extents at all or has empty last
* indirect extent ! Create a fake extent . . . */
extent . extLocation . logicalBlockNum = 0 ;
extent . extLocation . partitionReferenceNum = 0 ;
2007-07-21 04:37:18 -07:00
extent . extLength = EXT_NOT_RECORDED_NOT_ALLOCATED ;
2007-07-19 01:47:43 -07:00
} else {
2007-05-08 00:35:14 -07:00
epos . offset - = adsize ;
2007-05-08 00:35:21 -07:00
etype = udf_next_aext ( inode , & epos ,
2007-07-19 01:47:43 -07:00
& extent . extLocation ,
& extent . extLength , 0 ) ;
2007-05-08 00:35:21 -07:00
extent . extLength | = etype < < 30 ;
2005-04-16 15:20:36 -07:00
}
2007-07-19 01:47:43 -07:00
udf_extend_file ( inode , & epos , & extent ,
2007-07-21 04:37:18 -07:00
offset + ( ( inode - > i_size & ( sb - > s_blocksize - 1 ) ) ! = 0 ) ) ;
2005-04-16 15:20:36 -07:00
}
}
UDF_I_LENEXTENTS ( inode ) = inode - > i_size ;
2007-05-08 00:35:16 -07:00
brelse ( epos . bh ) ;
2005-04-16 15:20:36 -07:00
}