2005-04-17 02:20:36 +04:00
/*
* linux / fs / hfsplus / extents . c
*
* Copyright ( C ) 2001
* Brad Boyer ( flar @ allandria . com )
* ( C ) 2003 Ardis Technologies < roman @ ardistech . com >
*
* Handling of Extents both in catalog and extents overflow trees
*/
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/pagemap.h>
# include "hfsplus_fs.h"
# include "hfsplus_raw.h"
/* Compare two extents keys, returns 0 on same, pos/neg for difference */
2006-01-19 04:43:08 +03:00
int hfsplus_ext_cmp_key ( const hfsplus_btree_key * k1 ,
const hfsplus_btree_key * k2 )
2005-04-17 02:20:36 +04:00
{
__be32 k1id , k2id ;
__be32 k1s , k2s ;
k1id = k1 - > ext . cnid ;
k2id = k2 - > ext . cnid ;
if ( k1id ! = k2id )
return be32_to_cpu ( k1id ) < be32_to_cpu ( k2id ) ? - 1 : 1 ;
if ( k1 - > ext . fork_type ! = k2 - > ext . fork_type )
return k1 - > ext . fork_type < k2 - > ext . fork_type ? - 1 : 1 ;
k1s = k1 - > ext . start_block ;
k2s = k2 - > ext . start_block ;
if ( k1s = = k2s )
return 0 ;
return be32_to_cpu ( k1s ) < be32_to_cpu ( k2s ) ? - 1 : 1 ;
}
static void hfsplus_ext_build_key ( hfsplus_btree_key * key , u32 cnid ,
u32 block , u8 type )
{
key - > key_len = cpu_to_be16 ( HFSPLUS_EXT_KEYLEN - 2 ) ;
key - > ext . cnid = cpu_to_be32 ( cnid ) ;
key - > ext . start_block = cpu_to_be32 ( block ) ;
key - > ext . fork_type = type ;
key - > ext . pad = 0 ;
}
static u32 hfsplus_ext_find_block ( struct hfsplus_extent * ext , u32 off )
{
int i ;
u32 count ;
for ( i = 0 ; i < 8 ; ext + + , i + + ) {
count = be32_to_cpu ( ext - > block_count ) ;
if ( off < count )
return be32_to_cpu ( ext - > start_block ) + off ;
off - = count ;
}
/* panic? */
return 0 ;
}
static int hfsplus_ext_block_count ( struct hfsplus_extent * ext )
{
int i ;
u32 count = 0 ;
for ( i = 0 ; i < 8 ; ext + + , i + + )
count + = be32_to_cpu ( ext - > block_count ) ;
return count ;
}
static u32 hfsplus_ext_lastblock ( struct hfsplus_extent * ext )
{
int i ;
ext + = 7 ;
for ( i = 0 ; i < 7 ; ext - - , i + + )
if ( ext - > block_count )
break ;
return be32_to_cpu ( ext - > start_block ) + be32_to_cpu ( ext - > block_count ) ;
}
2013-05-01 02:27:56 +04:00
static int __hfsplus_ext_write_extent ( struct inode * inode ,
2010-12-16 19:08:38 +03:00
struct hfs_find_data * fd )
2005-04-17 02:20:36 +04:00
{
2010-10-01 07:43:31 +04:00
struct hfsplus_inode_info * hip = HFSPLUS_I ( inode ) ;
2005-04-17 02:20:36 +04:00
int res ;
2010-10-01 07:46:31 +04:00
WARN_ON ( ! mutex_is_locked ( & hip - > extents_lock ) ) ;
2010-10-01 07:43:31 +04:00
hfsplus_ext_build_key ( fd - > search_key , inode - > i_ino , hip - > cached_start ,
HFSPLUS_IS_RSRC ( inode ) ?
HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA ) ;
2013-02-28 05:03:04 +04:00
res = hfs_brec_find ( fd , hfs_find_rec_by_key ) ;
2010-11-23 16:38:13 +03:00
if ( hip - > extent_state & HFSPLUS_EXT_NEW ) {
2005-04-17 02:20:36 +04:00
if ( res ! = - ENOENT )
2013-05-01 02:27:56 +04:00
return res ;
2010-10-01 07:43:31 +04:00
hfs_brec_insert ( fd , hip - > cached_extents ,
sizeof ( hfsplus_extent_rec ) ) ;
2010-11-23 16:38:13 +03:00
hip - > extent_state & = ~ ( HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW ) ;
2005-04-17 02:20:36 +04:00
} else {
if ( res )
2013-05-01 02:27:56 +04:00
return res ;
2010-10-01 07:43:31 +04:00
hfs_bnode_write ( fd - > bnode , hip - > cached_extents ,
fd - > entryoffset , fd - > entrylength ) ;
2010-11-23 16:38:13 +03:00
hip - > extent_state & = ~ HFSPLUS_EXT_DIRTY ;
2005-04-17 02:20:36 +04:00
}
2010-11-23 16:38:15 +03:00
/*
* We can ' t just use hfsplus_mark_inode_dirty here , because we
* also get called from hfsplus_write_inode , which should not
* redirty the inode . Instead the callers have to be careful
* to explicily mark the inode dirty , too .
*/
set_bit ( HFSPLUS_I_EXT_DIRTY , & hip - > flags ) ;
2013-05-01 02:27:56 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2011-07-06 02:30:00 +04:00
static int hfsplus_ext_write_extent_locked ( struct inode * inode )
2005-04-17 02:20:36 +04:00
{
2013-05-01 02:27:56 +04:00
int res = 0 ;
2011-07-06 02:30:00 +04:00
2010-11-23 16:38:13 +03:00
if ( HFSPLUS_I ( inode ) - > extent_state & HFSPLUS_EXT_DIRTY ) {
2005-04-17 02:20:36 +04:00
struct hfs_find_data fd ;
2011-07-06 02:30:00 +04:00
res = hfs_find_init ( HFSPLUS_SB ( inode - > i_sb ) - > ext_tree , & fd ) ;
if ( res )
return res ;
2013-05-01 02:27:56 +04:00
res = __hfsplus_ext_write_extent ( inode , & fd ) ;
2011-07-06 02:30:00 +04:00
hfs_find_exit ( & fd ) ;
2005-04-17 02:20:36 +04:00
}
2013-05-01 02:27:56 +04:00
return res ;
2005-04-17 02:20:36 +04:00
}
2011-07-06 02:30:00 +04:00
int hfsplus_ext_write_extent ( struct inode * inode )
2010-10-01 07:46:31 +04:00
{
2011-07-06 02:30:00 +04:00
int res ;
2010-10-01 07:46:31 +04:00
mutex_lock ( & HFSPLUS_I ( inode ) - > extents_lock ) ;
2011-07-06 02:30:00 +04:00
res = hfsplus_ext_write_extent_locked ( inode ) ;
2010-10-01 07:46:31 +04:00
mutex_unlock ( & HFSPLUS_I ( inode ) - > extents_lock ) ;
2011-07-06 02:30:00 +04:00
return res ;
2010-10-01 07:46:31 +04:00
}
2005-04-17 02:20:36 +04:00
static inline int __hfsplus_ext_read_extent ( struct hfs_find_data * fd ,
struct hfsplus_extent * extent ,
u32 cnid , u32 block , u8 type )
{
int res ;
hfsplus_ext_build_key ( fd - > search_key , cnid , block , type ) ;
fd - > key - > ext . cnid = 0 ;
2013-02-28 05:03:04 +04:00
res = hfs_brec_find ( fd , hfs_find_rec_by_key ) ;
2005-04-17 02:20:36 +04:00
if ( res & & res ! = - ENOENT )
return res ;
if ( fd - > key - > ext . cnid ! = fd - > search_key - > ext . cnid | |
fd - > key - > ext . fork_type ! = fd - > search_key - > ext . fork_type )
return - ENOENT ;
if ( fd - > entrylength ! = sizeof ( hfsplus_extent_rec ) )
return - EIO ;
2010-12-16 19:08:38 +03:00
hfs_bnode_read ( fd - > bnode , extent , fd - > entryoffset ,
sizeof ( hfsplus_extent_rec ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-12-16 19:08:38 +03:00
static inline int __hfsplus_ext_cache_extent ( struct hfs_find_data * fd ,
struct inode * inode , u32 block )
2005-04-17 02:20:36 +04:00
{
2010-10-01 07:43:31 +04:00
struct hfsplus_inode_info * hip = HFSPLUS_I ( inode ) ;
2005-04-17 02:20:36 +04:00
int res ;
2010-10-01 07:46:31 +04:00
WARN_ON ( ! mutex_is_locked ( & hip - > extents_lock ) ) ;
2013-05-01 02:27:56 +04:00
if ( hip - > extent_state & HFSPLUS_EXT_DIRTY ) {
res = __hfsplus_ext_write_extent ( inode , fd ) ;
if ( res )
return res ;
}
2005-04-17 02:20:36 +04:00
2010-10-01 07:43:31 +04:00
res = __hfsplus_ext_read_extent ( fd , hip - > cached_extents , inode - > i_ino ,
block , HFSPLUS_IS_RSRC ( inode ) ?
HFSPLUS_TYPE_RSRC :
HFSPLUS_TYPE_DATA ) ;
2005-04-17 02:20:36 +04:00
if ( ! res ) {
2010-10-01 07:43:31 +04:00
hip - > cached_start = be32_to_cpu ( fd - > key - > ext . start_block ) ;
2010-12-16 19:08:38 +03:00
hip - > cached_blocks =
hfsplus_ext_block_count ( hip - > cached_extents ) ;
2005-04-17 02:20:36 +04:00
} else {
2010-10-01 07:43:31 +04:00
hip - > cached_start = hip - > cached_blocks = 0 ;
2010-11-23 16:38:13 +03:00
hip - > extent_state & = ~ ( HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW ) ;
2005-04-17 02:20:36 +04:00
}
return res ;
}
static int hfsplus_ext_read_extent ( struct inode * inode , u32 block )
{
2010-10-01 07:43:31 +04:00
struct hfsplus_inode_info * hip = HFSPLUS_I ( inode ) ;
2005-04-17 02:20:36 +04:00
struct hfs_find_data fd ;
int res ;
2010-10-01 07:43:31 +04:00
if ( block > = hip - > cached_start & &
block < hip - > cached_start + hip - > cached_blocks )
2005-04-17 02:20:36 +04:00
return 0 ;
2011-07-06 02:29:59 +04:00
res = hfs_find_init ( HFSPLUS_SB ( inode - > i_sb ) - > ext_tree , & fd ) ;
if ( ! res ) {
res = __hfsplus_ext_cache_extent ( & fd , inode , block ) ;
hfs_find_exit ( & fd ) ;
}
2005-04-17 02:20:36 +04:00
return res ;
}
/* Get a block at iblock for inode, possibly allocating if create */
int hfsplus_get_block ( struct inode * inode , sector_t iblock ,
struct buffer_head * bh_result , int create )
{
2010-10-01 07:42:59 +04:00
struct super_block * sb = inode - > i_sb ;
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( sb ) ;
2010-10-01 07:43:31 +04:00
struct hfsplus_inode_info * hip = HFSPLUS_I ( inode ) ;
2005-04-17 02:20:36 +04:00
int res = - EIO ;
u32 ablock , dblock , mask ;
2011-02-16 11:34:17 +03:00
sector_t sector ;
2010-11-23 16:38:15 +03:00
int was_dirty = 0 ;
2005-04-17 02:20:36 +04:00
/* Convert inode block to disk allocation block */
2010-10-01 07:42:59 +04:00
ablock = iblock > > sbi - > fs_shift ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:43:31 +04:00
if ( iblock > = hip - > fs_blocks ) {
if ( iblock > hip - > fs_blocks | | ! create )
2005-04-17 02:20:36 +04:00
return - EIO ;
2010-10-01 07:43:31 +04:00
if ( ablock > = hip - > alloc_blocks ) {
2014-06-07 01:36:28 +04:00
res = hfsplus_file_extend ( inode , false ) ;
2005-04-17 02:20:36 +04:00
if ( res )
return res ;
}
} else
create = 0 ;
2010-10-01 07:43:31 +04:00
if ( ablock < hip - > first_blocks ) {
dblock = hfsplus_ext_find_block ( hip - > first_extents , ablock ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
2008-10-19 07:28:02 +04:00
if ( inode - > i_ino = = HFSPLUS_EXT_CNID )
return - EIO ;
2010-10-01 07:43:31 +04:00
mutex_lock ( & hip - > extents_lock ) ;
2010-11-23 16:38:15 +03:00
/*
* hfsplus_ext_read_extent will write out a cached extent into
* the extents btree . In that case we may have to mark the inode
* dirty even for a pure read of an extent here .
*/
was_dirty = ( hip - > extent_state & HFSPLUS_EXT_DIRTY ) ;
2005-04-17 02:20:36 +04:00
res = hfsplus_ext_read_extent ( inode , ablock ) ;
2010-11-23 16:38:15 +03:00
if ( res ) {
2010-10-01 07:43:31 +04:00
mutex_unlock ( & hip - > extents_lock ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2010-11-23 16:38:15 +03:00
dblock = hfsplus_ext_find_block ( hip - > cached_extents ,
ablock - hip - > cached_start ) ;
2010-10-01 07:43:31 +04:00
mutex_unlock ( & hip - > extents_lock ) ;
2005-04-17 02:20:36 +04:00
done :
2013-05-01 02:27:54 +04:00
hfs_dbg ( EXTENT , " get_block(%lu): %llu - %u \n " ,
2010-12-16 19:08:38 +03:00
inode - > i_ino , ( long long ) iblock , dblock ) ;
2011-02-16 11:34:17 +03:00
2010-10-01 07:42:59 +04:00
mask = ( 1 < < sbi - > fs_shift ) - 1 ;
2011-02-16 11:34:17 +03:00
sector = ( ( sector_t ) dblock < < sbi - > fs_shift ) +
sbi - > blockoffset + ( iblock & mask ) ;
map_bh ( bh_result , sb , sector ) ;
2005-04-17 02:20:36 +04:00
if ( create ) {
set_buffer_new ( bh_result ) ;
2010-10-01 07:43:31 +04:00
hip - > phys_size + = sb - > s_blocksize ;
hip - > fs_blocks + + ;
2005-04-17 02:20:36 +04:00
inode_add_bytes ( inode , sb - > s_blocksize ) ;
}
2010-11-23 16:38:15 +03:00
if ( create | | was_dirty )
mark_inode_dirty ( inode ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void hfsplus_dump_extent ( struct hfsplus_extent * extent )
{
int i ;
2013-05-01 02:27:54 +04:00
hfs_dbg ( EXTENT , " " ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 8 ; i + + )
2013-05-01 02:27:54 +04:00
hfs_dbg_cont ( EXTENT , " %u:%u " ,
be32_to_cpu ( extent [ i ] . start_block ) ,
be32_to_cpu ( extent [ i ] . block_count ) ) ;
hfs_dbg_cont ( EXTENT , " \n " ) ;
2005-04-17 02:20:36 +04:00
}
static int hfsplus_add_extent ( struct hfsplus_extent * extent , u32 offset ,
u32 alloc_block , u32 block_count )
{
u32 count , start ;
int i ;
hfsplus_dump_extent ( extent ) ;
for ( i = 0 ; i < 8 ; extent + + , i + + ) {
count = be32_to_cpu ( extent - > block_count ) ;
if ( offset = = count ) {
start = be32_to_cpu ( extent - > start_block ) ;
if ( alloc_block ! = start + count ) {
if ( + + i > = 8 )
return - ENOSPC ;
extent + + ;
extent - > start_block = cpu_to_be32 ( alloc_block ) ;
} else
block_count + = count ;
extent - > block_count = cpu_to_be32 ( block_count ) ;
return 0 ;
} else if ( offset < count )
break ;
offset - = count ;
}
/* panic? */
return - EIO ;
}
static int hfsplus_free_extents ( struct super_block * sb ,
struct hfsplus_extent * extent ,
u32 offset , u32 block_nr )
{
u32 count , start ;
int i ;
2012-12-21 03:05:25 +04:00
int err = 0 ;
2005-04-17 02:20:36 +04:00
hfsplus_dump_extent ( extent ) ;
for ( i = 0 ; i < 8 ; extent + + , i + + ) {
count = be32_to_cpu ( extent - > block_count ) ;
if ( offset = = count )
goto found ;
else if ( offset < count )
break ;
offset - = count ;
}
/* panic? */
return - EIO ;
found :
for ( ; ; ) {
start = be32_to_cpu ( extent - > start_block ) ;
if ( count < = block_nr ) {
2012-12-21 03:05:25 +04:00
err = hfsplus_block_free ( sb , start , count ) ;
if ( err ) {
2013-05-01 02:27:55 +04:00
pr_err ( " can't free extent \n " ) ;
2013-05-01 02:27:54 +04:00
hfs_dbg ( EXTENT , " start: %u count: %u \n " ,
2012-12-21 03:05:25 +04:00
start , count ) ;
}
2005-04-17 02:20:36 +04:00
extent - > block_count = 0 ;
extent - > start_block = 0 ;
block_nr - = count ;
} else {
count - = block_nr ;
2012-12-21 03:05:25 +04:00
err = hfsplus_block_free ( sb , start + count , block_nr ) ;
if ( err ) {
2013-05-01 02:27:55 +04:00
pr_err ( " can't free extent \n " ) ;
2013-05-01 02:27:54 +04:00
hfs_dbg ( EXTENT , " start: %u count: %u \n " ,
2012-12-21 03:05:25 +04:00
start , count ) ;
}
2005-04-17 02:20:36 +04:00
extent - > block_count = cpu_to_be32 ( count ) ;
block_nr = 0 ;
}
2012-12-21 03:05:25 +04:00
if ( ! block_nr | | ! i ) {
/*
* Try to free all extents and
* return only last error
*/
return err ;
}
2005-04-17 02:20:36 +04:00
i - - ;
extent - - ;
count = be32_to_cpu ( extent - > block_count ) ;
}
}
2010-12-16 19:08:38 +03:00
int hfsplus_free_fork ( struct super_block * sb , u32 cnid ,
struct hfsplus_fork_raw * fork , int type )
2005-04-17 02:20:36 +04:00
{
struct hfs_find_data fd ;
hfsplus_extent_rec ext_entry ;
u32 total_blocks , blocks , start ;
int res , i ;
total_blocks = be32_to_cpu ( fork - > total_blocks ) ;
if ( ! total_blocks )
return 0 ;
blocks = 0 ;
for ( i = 0 ; i < 8 ; i + + )
blocks + = be32_to_cpu ( fork - > extents [ i ] . block_count ) ;
res = hfsplus_free_extents ( sb , fork - > extents , blocks , blocks ) ;
if ( res )
return res ;
if ( total_blocks = = blocks )
return 0 ;
2011-07-06 02:29:59 +04:00
res = hfs_find_init ( HFSPLUS_SB ( sb ) - > ext_tree , & fd ) ;
if ( res )
return res ;
2005-04-17 02:20:36 +04:00
do {
res = __hfsplus_ext_read_extent ( & fd , ext_entry , cnid ,
total_blocks , type ) ;
if ( res )
break ;
start = be32_to_cpu ( fd . key - > ext . start_block ) ;
hfsplus_free_extents ( sb , ext_entry ,
total_blocks - start ,
total_blocks ) ;
hfs_brec_remove ( & fd ) ;
total_blocks = start ;
} while ( total_blocks > blocks ) ;
hfs_find_exit ( & fd ) ;
return res ;
}
2014-06-07 01:36:28 +04:00
int hfsplus_file_extend ( struct inode * inode , bool zeroout )
2005-04-17 02:20:36 +04:00
{
struct super_block * sb = inode - > i_sb ;
2010-10-01 07:42:59 +04:00
struct hfsplus_sb_info * sbi = HFSPLUS_SB ( sb ) ;
2010-10-01 07:43:31 +04:00
struct hfsplus_inode_info * hip = HFSPLUS_I ( inode ) ;
2005-04-17 02:20:36 +04:00
u32 start , len , goal ;
int res ;
2011-02-02 19:40:33 +03:00
if ( sbi - > alloc_file - > i_size * 8 <
sbi - > total_blocks - sbi - > free_blocks + 8 ) {
2010-12-16 19:08:39 +03:00
/* extend alloc file */
2014-06-07 01:36:31 +04:00
pr_err ( " extend alloc file! (%llu,%u,%u) \n " ,
sbi - > alloc_file - > i_size * 8 ,
sbi - > total_blocks , sbi - > free_blocks ) ;
2005-04-17 02:20:36 +04:00
return - ENOSPC ;
}
2010-10-01 07:43:31 +04:00
mutex_lock ( & hip - > extents_lock ) ;
if ( hip - > alloc_blocks = = hip - > first_blocks )
goal = hfsplus_ext_lastblock ( hip - > first_extents ) ;
2005-04-17 02:20:36 +04:00
else {
2010-10-01 07:43:31 +04:00
res = hfsplus_ext_read_extent ( inode , hip - > alloc_blocks ) ;
2005-04-17 02:20:36 +04:00
if ( res )
goto out ;
2010-10-01 07:43:31 +04:00
goal = hfsplus_ext_lastblock ( hip - > cached_extents ) ;
2005-04-17 02:20:36 +04:00
}
2010-10-01 07:43:31 +04:00
len = hip - > clump_blocks ;
2010-10-01 07:42:59 +04:00
start = hfsplus_block_allocate ( sb , sbi - > total_blocks , goal , & len ) ;
if ( start > = sbi - > total_blocks ) {
2005-04-17 02:20:36 +04:00
start = hfsplus_block_allocate ( sb , goal , 0 , & len ) ;
if ( start > = goal ) {
res = - ENOSPC ;
goto out ;
}
}
2014-06-07 01:36:28 +04:00
if ( zeroout ) {
res = sb_issue_zeroout ( sb , start , len , GFP_NOFS ) ;
if ( res )
goto out ;
}
2013-05-01 02:27:54 +04:00
hfs_dbg ( EXTENT , " extend %lu: %u,%u \n " , inode - > i_ino , start , len ) ;
2010-10-01 07:43:31 +04:00
if ( hip - > alloc_blocks < = hip - > first_blocks ) {
if ( ! hip - > first_blocks ) {
2013-05-01 02:27:54 +04:00
hfs_dbg ( EXTENT , " first extents \n " ) ;
2005-04-17 02:20:36 +04:00
/* no extents yet */
2010-10-01 07:43:31 +04:00
hip - > first_extents [ 0 ] . start_block = cpu_to_be32 ( start ) ;
hip - > first_extents [ 0 ] . block_count = cpu_to_be32 ( len ) ;
2005-04-17 02:20:36 +04:00
res = 0 ;
} else {
/* try to append to extents in inode */
2010-10-01 07:43:31 +04:00
res = hfsplus_add_extent ( hip - > first_extents ,
hip - > alloc_blocks ,
2005-04-17 02:20:36 +04:00
start , len ) ;
if ( res = = - ENOSPC )
goto insert_extent ;
}
if ( ! res ) {
2010-10-01 07:43:31 +04:00
hfsplus_dump_extent ( hip - > first_extents ) ;
hip - > first_blocks + = len ;
2005-04-17 02:20:36 +04:00
}
} else {
2010-10-01 07:43:31 +04:00
res = hfsplus_add_extent ( hip - > cached_extents ,
hip - > alloc_blocks - hip - > cached_start ,
2005-04-17 02:20:36 +04:00
start , len ) ;
if ( ! res ) {
2010-10-01 07:43:31 +04:00
hfsplus_dump_extent ( hip - > cached_extents ) ;
2010-11-23 16:38:13 +03:00
hip - > extent_state | = HFSPLUS_EXT_DIRTY ;
2010-10-01 07:43:31 +04:00
hip - > cached_blocks + = len ;
2005-04-17 02:20:36 +04:00
} else if ( res = = - ENOSPC )
goto insert_extent ;
}
out :
if ( ! res ) {
2010-10-01 07:43:31 +04:00
hip - > alloc_blocks + = len ;
2014-04-04 01:50:34 +04:00
mutex_unlock ( & hip - > extents_lock ) ;
2010-11-23 16:38:15 +03:00
hfsplus_mark_inode_dirty ( inode , HFSPLUS_I_ALLOC_DIRTY ) ;
2014-04-04 01:50:34 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2014-04-04 01:50:34 +04:00
mutex_unlock ( & hip - > extents_lock ) ;
2005-04-17 02:20:36 +04:00
return res ;
insert_extent :
2013-05-01 02:27:54 +04:00
hfs_dbg ( EXTENT , " insert new extent \n " ) ;
2011-07-06 02:30:00 +04:00
res = hfsplus_ext_write_extent_locked ( inode ) ;
if ( res )
goto out ;
2005-04-17 02:20:36 +04:00
2010-10-01 07:43:31 +04:00
memset ( hip - > cached_extents , 0 , sizeof ( hfsplus_extent_rec ) ) ;
hip - > cached_extents [ 0 ] . start_block = cpu_to_be32 ( start ) ;
hip - > cached_extents [ 0 ] . block_count = cpu_to_be32 ( len ) ;
hfsplus_dump_extent ( hip - > cached_extents ) ;
2010-11-23 16:38:13 +03:00
hip - > extent_state | = HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW ;
2010-10-01 07:43:31 +04:00
hip - > cached_start = hip - > alloc_blocks ;
hip - > cached_blocks = len ;
2005-04-17 02:20:36 +04:00
res = 0 ;
goto out ;
}
void hfsplus_file_truncate ( struct inode * inode )
{
struct super_block * sb = inode - > i_sb ;
2010-10-01 07:43:31 +04:00
struct hfsplus_inode_info * hip = HFSPLUS_I ( inode ) ;
2005-04-17 02:20:36 +04:00
struct hfs_find_data fd ;
u32 alloc_cnt , blk_cnt , start ;
int res ;
2013-05-01 02:27:54 +04:00
hfs_dbg ( INODE , " truncate: %lu, %llu -> %llu \n " ,
inode - > i_ino , ( long long ) hip - > phys_size , inode - > i_size ) ;
2010-10-01 07:43:31 +04:00
if ( inode - > i_size > hip - > phys_size ) {
2005-04-17 02:20:36 +04:00
struct address_space * mapping = inode - > i_mapping ;
struct page * page ;
2007-10-16 12:25:09 +04:00
void * fsdata ;
2013-04-18 02:58:33 +04:00
loff_t size = inode - > i_size ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:25:09 +04:00
res = pagecache_write_begin ( NULL , mapping , size , 0 ,
AOP_FLAG_UNINTERRUPTIBLE ,
& page , & fsdata ) ;
2005-04-17 02:20:36 +04:00
if ( res )
2007-10-16 12:25:09 +04:00
return ;
2010-12-16 19:08:38 +03:00
res = pagecache_write_end ( NULL , mapping , size ,
0 , 0 , page , fsdata ) ;
2007-10-16 12:25:09 +04:00
if ( res < 0 )
return ;
2005-04-17 02:20:36 +04:00
mark_inode_dirty ( inode ) ;
return ;
2010-10-01 07:43:31 +04:00
} else if ( inode - > i_size = = hip - > phys_size )
2005-08-02 08:11:40 +04:00
return ;
2010-10-01 07:42:59 +04:00
blk_cnt = ( inode - > i_size + HFSPLUS_SB ( sb ) - > alloc_blksz - 1 ) > >
HFSPLUS_SB ( sb ) - > alloc_blksz_shift ;
2014-04-04 01:50:34 +04:00
mutex_lock ( & hip - > extents_lock ) ;
2010-10-01 07:43:31 +04:00
alloc_cnt = hip - > alloc_blocks ;
2005-04-17 02:20:36 +04:00
if ( blk_cnt = = alloc_cnt )
2014-04-04 01:50:34 +04:00
goto out_unlock ;
2005-04-17 02:20:36 +04:00
2011-07-06 02:29:59 +04:00
res = hfs_find_init ( HFSPLUS_SB ( sb ) - > ext_tree , & fd ) ;
if ( res ) {
mutex_unlock ( & hip - > extents_lock ) ;
/* XXX: We lack error handling of hfsplus_file_truncate() */
return ;
}
2005-04-17 02:20:36 +04:00
while ( 1 ) {
2010-10-01 07:43:31 +04:00
if ( alloc_cnt = = hip - > first_blocks ) {
hfsplus_free_extents ( sb , hip - > first_extents ,
2005-04-17 02:20:36 +04:00
alloc_cnt , alloc_cnt - blk_cnt ) ;
2010-10-01 07:43:31 +04:00
hfsplus_dump_extent ( hip - > first_extents ) ;
hip - > first_blocks = blk_cnt ;
2005-04-17 02:20:36 +04:00
break ;
}
res = __hfsplus_ext_cache_extent ( & fd , inode , alloc_cnt ) ;
if ( res )
break ;
2010-10-01 07:43:31 +04:00
start = hip - > cached_start ;
hfsplus_free_extents ( sb , hip - > cached_extents ,
2005-04-17 02:20:36 +04:00
alloc_cnt - start , alloc_cnt - blk_cnt ) ;
2010-10-01 07:43:31 +04:00
hfsplus_dump_extent ( hip - > cached_extents ) ;
2005-04-17 02:20:36 +04:00
if ( blk_cnt > start ) {
2010-11-23 16:38:13 +03:00
hip - > extent_state | = HFSPLUS_EXT_DIRTY ;
2005-04-17 02:20:36 +04:00
break ;
}
alloc_cnt = start ;
2010-10-01 07:43:31 +04:00
hip - > cached_start = hip - > cached_blocks = 0 ;
2010-11-23 16:38:13 +03:00
hip - > extent_state & = ~ ( HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW ) ;
2005-04-17 02:20:36 +04:00
hfs_brec_remove ( & fd ) ;
}
hfs_find_exit ( & fd ) ;
2010-10-01 07:43:31 +04:00
hip - > alloc_blocks = blk_cnt ;
2014-04-04 01:50:34 +04:00
out_unlock :
mutex_unlock ( & hip - > extents_lock ) ;
2010-10-01 07:43:31 +04:00
hip - > phys_size = inode - > i_size ;
2010-12-16 19:08:38 +03:00
hip - > fs_blocks = ( inode - > i_size + sb - > s_blocksize - 1 ) > >
sb - > s_blocksize_bits ;
2010-10-01 07:43:31 +04:00
inode_set_bytes ( inode , hip - > fs_blocks < < sb - > s_blocksize_bits ) ;
2010-11-23 16:38:15 +03:00
hfsplus_mark_inode_dirty ( inode , HFSPLUS_I_ALLOC_DIRTY ) ;
2005-04-17 02:20:36 +04:00
}