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/buffer_head.h>
# include "udf_i.h"
# include "udf_sb.h"
2007-07-19 12:47:43 +04:00
static void extent_trunc ( struct inode * inode , struct extent_position * epos ,
2008-10-15 14:29:03 +04:00
struct kernel_lb_addr * eloc , int8_t etype , uint32_t elen ,
2007-07-19 12:47:43 +04:00
uint32_t nelen )
2005-04-17 02:20:36 +04:00
{
2008-10-15 14:28:03 +04:00
struct kernel_lb_addr neloc = { } ;
2007-07-21 15:37:18 +04:00
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-17 02:20:36 +04:00
2007-07-19 12:47:43 +04:00
if ( nelen ) {
if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) ) {
udf_free_blocks ( inode - > i_sb , inode , eloc , 0 ,
last_block ) ;
2005-04-17 02:20:36 +04:00
etype = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) ;
2007-07-19 12:47:43 +04:00
} else
2008-10-15 14:29:03 +04:00
neloc = * eloc ;
2005-04-17 02:20:36 +04:00
nelen = ( etype < < 30 ) | nelen ;
}
2007-07-19 12:47:43 +04:00
if ( elen ! = nelen ) {
2008-10-15 14:29:03 +04:00
udf_write_aext ( inode , epos , & neloc , nelen , 0 ) ;
2007-07-19 12:47:43 +04:00
if ( last_block - first_block > 0 ) {
2005-04-17 02:20:36 +04:00
if ( etype = = ( EXT_RECORDED_ALLOCATED > > 30 ) )
mark_inode_dirty ( inode ) ;
if ( etype ! = ( EXT_NOT_RECORDED_NOT_ALLOCATED > > 30 ) )
2007-07-19 12:47:43 +04:00
udf_free_blocks ( inode - > i_sb , inode , eloc ,
first_block ,
last_block - first_block ) ;
2005-04-17 02:20:36 +04:00
}
}
}
2007-06-16 21:16:14 +04: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-17 02:20:36 +04:00
{
2007-07-21 15:37:18 +04:00
struct extent_position epos = { } ;
2008-10-15 14:28:03 +04:00
struct kernel_lb_addr eloc ;
2007-05-08 11:35:14 +04:00
uint32_t elen , nelen ;
2005-04-17 02:20:36 +04:00
uint64_t lbcount = 0 ;
int8_t etype = - 1 , netype ;
int adsize ;
2008-02-08 15:20:44 +03:00
struct udf_inode_info * iinfo = UDF_I ( inode ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_IN_ICB | |
inode - > i_size = = iinfo - > i_lenExtents )
2007-06-16 21:16:14 +04:00
return ;
/* Are we going to delete the file anyway? */
if ( inode - > i_nlink = = 0 )
2005-04-17 02:20:36 +04:00
return ;
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_SHORT )
2008-10-15 14:28:03 +04:00
adsize = sizeof ( struct short_ad ) ;
2008-02-08 15:20:44 +03:00
else if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_LONG )
2008-10-15 14:28:03 +04:00
adsize = sizeof ( struct long_ad ) ;
2005-04-17 02:20:36 +04:00
else
2007-06-16 21:16:14 +04:00
BUG ( ) ;
2005-04-17 02:20:36 +04:00
2007-05-08 11:35:14 +04:00
/* Find the last extent in the file */
2007-07-19 12:47:43 +04:00
while ( ( netype = udf_next_aext ( inode , & epos , & eloc , & elen , 1 ) ) ! = - 1 ) {
2005-04-17 02:20:36 +04:00
etype = netype ;
lbcount + = elen ;
2007-06-16 21:16:14 +04:00
if ( lbcount > inode - > i_size ) {
if ( lbcount - inode - > i_size > = inode - > i_sb - > s_blocksize )
2011-10-10 12:08:05 +04:00
udf_warn ( inode - > i_sb ,
" Too long extent after EOF in inode %u: i_size: %lld lbcount: %lld extent %u+%u \n " ,
( unsigned ) inode - > i_ino ,
( long long ) inode - > i_size ,
( long long ) lbcount ,
( unsigned ) eloc . logicalBlockNum ,
( unsigned ) elen ) ;
2005-04-17 02:20:36 +04:00
nelen = elen - ( lbcount - inode - > i_size ) ;
2007-05-08 11:35:14 +04:00
epos . offset - = adsize ;
2008-10-15 14:29:03 +04:00
extent_trunc ( inode , & epos , & eloc , etype , elen , nelen ) ;
2007-05-08 11:35:14 +04:00
epos . offset + = adsize ;
2007-06-16 21:16:14 +04:00
if ( udf_next_aext ( inode , & epos , & eloc , & elen , 1 ) ! = - 1 )
2011-10-10 12:08:05 +04:00
udf_err ( inode - > i_sb ,
" Extent after EOF in inode %u \n " ,
( unsigned ) inode - > i_ino ) ;
2007-06-16 21:16:14 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
}
2007-06-16 21:16:14 +04:00
/* This inode entry is in-memory only and thus we don't have to mark
* the inode dirty */
2008-02-08 15:20:44 +03:00
iinfo - > i_lenExtents = inode - > i_size ;
2007-06-16 21:16:14 +04:00
brelse ( epos . bh ) ;
}
void udf_discard_prealloc ( struct inode * inode )
{
2007-07-19 12:47:43 +04:00
struct extent_position epos = { NULL , 0 , { 0 , 0 } } ;
2008-10-15 14:28:03 +04:00
struct kernel_lb_addr eloc ;
2007-06-16 21:16:14 +04:00
uint32_t elen ;
uint64_t lbcount = 0 ;
int8_t etype = - 1 , netype ;
int adsize ;
2008-02-08 15:20:44 +03:00
struct udf_inode_info * iinfo = UDF_I ( inode ) ;
2007-06-16 21:16:14 +04:00
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_IN_ICB | |
inode - > i_size = = iinfo - > i_lenExtents )
2007-06-16 21:16:14 +04:00
return ;
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_SHORT )
2008-10-15 14:28:03 +04:00
adsize = sizeof ( struct short_ad ) ;
2008-02-08 15:20:44 +03:00
else if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_LONG )
2008-10-15 14:28:03 +04:00
adsize = sizeof ( struct long_ad ) ;
2007-06-16 21:16:14 +04:00
else
adsize = 0 ;
2008-02-08 15:20:44 +03:00
epos . block = iinfo - > i_location ;
2007-06-16 21:16:14 +04:00
/* 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 11:35:14 +04:00
if ( etype = = ( EXT_NOT_RECORDED_ALLOCATED > > 30 ) ) {
epos . offset - = adsize ;
2005-04-17 02:20:36 +04:00
lbcount - = elen ;
2008-10-15 14:29:03 +04:00
extent_trunc ( inode , & epos , & eloc , etype , elen , 0 ) ;
2007-06-16 21:16:14 +04:00
if ( ! epos . bh ) {
2008-02-08 15:20:44 +03:00
iinfo - > i_lenAlloc =
2008-02-08 15:20:36 +03:00
epos . offset -
udf_file_entry_alloc_offset ( inode ) ;
2005-04-17 02:20:36 +04:00
mark_inode_dirty ( inode ) ;
2007-06-16 21:16:14 +04:00
} else {
2007-07-19 12:47:43 +04:00
struct allocExtDesc * aed =
2007-07-21 15:37:18 +04:00
( struct allocExtDesc * ) ( epos . bh - > b_data ) ;
2007-07-19 12:47:43 +04:00
aed - > lengthAllocDescs =
2007-07-21 15:37:18 +04:00
cpu_to_le32 ( epos . offset -
sizeof ( struct allocExtDesc ) ) ;
if ( ! UDF_QUERY_FLAG ( inode - > i_sb , UDF_FLAG_STRICT ) | |
2008-02-08 15:20:30 +03:00
UDF_SB ( inode - > i_sb ) - > s_udfrev > = 0x0201 )
2007-05-08 11:35:14 +04:00
udf_update_tag ( epos . bh - > b_data , epos . offset ) ;
2005-04-17 02:20:36 +04:00
else
2007-07-19 12:47:43 +04:00
udf_update_tag ( epos . bh - > b_data ,
sizeof ( struct allocExtDesc ) ) ;
2007-05-08 11:35:14 +04:00
mark_buffer_dirty_inode ( epos . bh , inode ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-06-16 21:16:14 +04:00
/* This inode entry is in-memory only and thus we don't have to mark
* the inode dirty */
2008-02-08 15:20:44 +03:00
iinfo - > i_lenExtents = lbcount ;
2007-05-08 11:35:16 +04:00
brelse ( epos . bh ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-31 00:03:56 +03:00
static void udf_update_alloc_ext_desc ( struct inode * inode ,
struct extent_position * epos ,
u32 lenalloc )
{
struct super_block * sb = inode - > i_sb ;
struct udf_sb_info * sbi = UDF_SB ( sb ) ;
struct allocExtDesc * aed = ( struct allocExtDesc * ) ( epos - > bh - > b_data ) ;
int len = sizeof ( struct allocExtDesc ) ;
aed - > lengthAllocDescs = cpu_to_le32 ( lenalloc ) ;
if ( ! UDF_QUERY_FLAG ( sb , UDF_FLAG_STRICT ) | | sbi - > s_udfrev > = 0x0201 )
len + = lenalloc ;
udf_update_tag ( epos - > bh - > b_data , len ) ;
mark_buffer_dirty_inode ( epos - > bh , inode ) ;
}
2010-10-22 02:30:26 +04:00
/*
* Truncate extents of inode to inode - > i_size . This function can be used only
* for making file shorter . For making file longer , udf_extend_file ( ) has to
* be used .
*/
2007-07-19 12:47:43 +04:00
void udf_truncate_extents ( struct inode * inode )
2005-04-17 02:20:36 +04:00
{
2007-05-08 11:35:14 +04:00
struct extent_position epos ;
2008-10-15 14:28:03 +04:00
struct kernel_lb_addr eloc , neloc = { } ;
2007-05-08 11:35:14 +04:00
uint32_t elen , nelen = 0 , indirect_ext_len = 0 , lenalloc ;
2005-04-17 02:20:36 +04:00
int8_t etype ;
2007-05-08 11:35:21 +04:00
struct super_block * sb = inode - > i_sb ;
sector_t first_block = inode - > i_size > > sb - > s_blocksize_bits , offset ;
2007-05-08 11:35:13 +04:00
loff_t byte_offset ;
2005-04-17 02:20:36 +04:00
int adsize ;
2008-02-08 15:20:44 +03:00
struct udf_inode_info * iinfo = UDF_I ( inode ) ;
2005-04-17 02:20:36 +04:00
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_SHORT )
2008-10-15 14:28:03 +04:00
adsize = sizeof ( struct short_ad ) ;
2008-02-08 15:20:44 +03:00
else if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_LONG )
2008-10-15 14:28:03 +04:00
adsize = sizeof ( struct long_ad ) ;
2005-04-17 02:20:36 +04:00
else
2007-05-08 11:35:14 +04:00
BUG ( ) ;
2005-04-17 02:20:36 +04:00
2007-05-08 11:35:14 +04:00
etype = inode_bmap ( inode , first_block , & epos , & eloc , & elen , & offset ) ;
2007-07-21 15:37:18 +04:00
byte_offset = ( offset < < sb - > s_blocksize_bits ) +
( inode - > i_size & ( sb - > s_blocksize - 1 ) ) ;
2010-10-22 02:30:26 +04:00
if ( etype = = - 1 ) {
/* We should extend the file? */
WARN_ON ( byte_offset ) ;
return ;
}
epos . offset - = adsize ;
extent_trunc ( inode , & epos , & eloc , etype , elen , byte_offset ) ;
epos . offset + = adsize ;
if ( byte_offset )
lenalloc = epos . offset ;
else
lenalloc = epos . offset - adsize ;
2005-04-17 02:20:36 +04:00
2010-10-22 02:30:26 +04:00
if ( ! epos . bh )
lenalloc - = udf_file_entry_alloc_offset ( inode ) ;
else
lenalloc - = sizeof ( struct allocExtDesc ) ;
2007-05-08 11:35:21 +04:00
2010-10-22 02:30:26 +04:00
while ( ( etype = udf_current_aext ( inode , & epos , & eloc ,
& elen , 0 ) ) ! = - 1 ) {
if ( etype = = ( EXT_NEXT_EXTENT_ALLOCDECS > > 30 ) ) {
udf_write_aext ( inode , & epos , & neloc , nelen , 0 ) ;
if ( indirect_ext_len ) {
/* We managed to free all extents in the
* indirect extent - free it too */
BUG_ON ( ! epos . bh ) ;
2012-07-09 15:24:21 +04:00
udf_free_blocks ( sb , NULL , & epos . block ,
2010-10-22 02:30:26 +04:00
0 , indirect_ext_len ) ;
} else if ( ! epos . bh ) {
iinfo - > i_lenAlloc = lenalloc ;
mark_inode_dirty ( inode ) ;
} else
udf_update_alloc_ext_desc ( inode ,
& epos , lenalloc ) ;
brelse ( epos . bh ) ;
epos . offset = sizeof ( struct allocExtDesc ) ;
epos . block = eloc ;
epos . bh = udf_tread ( sb ,
udf_get_lb_pblock ( sb , & eloc , 0 ) ) ;
if ( elen )
indirect_ext_len =
( elen + sb - > s_blocksize - 1 ) > >
sb - > s_blocksize_bits ;
else
indirect_ext_len = 1 ;
} else {
extent_trunc ( inode , & epos , & eloc , etype , elen , 0 ) ;
epos . offset + = adsize ;
2005-04-17 02:20:36 +04:00
}
}
2010-10-22 02:30:26 +04:00
if ( indirect_ext_len ) {
BUG_ON ( ! epos . bh ) ;
2012-07-09 15:24:21 +04:00
udf_free_blocks ( sb , NULL , & epos . block , 0 , indirect_ext_len ) ;
2010-10-22 02:30:26 +04:00
} else if ( ! epos . bh ) {
iinfo - > i_lenAlloc = lenalloc ;
mark_inode_dirty ( inode ) ;
} else
udf_update_alloc_ext_desc ( inode , & epos , lenalloc ) ;
2008-02-08 15:20:44 +03:00
iinfo - > i_lenExtents = inode - > i_size ;
2005-04-17 02:20:36 +04:00
2007-05-08 11:35:16 +04:00
brelse ( epos . bh ) ;
2005-04-17 02:20:36 +04:00
}