2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2006-05-18 23:09:15 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2006-01-16 19:50:04 +03:00
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2006-09-01 19:05:15 +04:00
* of the GNU General Public License version 2.
2006-01-16 19:50:04 +03:00
*/
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.h>
2006-02-28 01:23:27 +03:00
# include <linux/gfs2_ondisk.h>
2006-03-28 23:14:04 +04:00
# include <linux/crc32.h>
2006-01-16 19:50:04 +03:00
# include "gfs2.h"
2006-02-28 01:23:27 +03:00
# include "incore.h"
2006-01-16 19:50:04 +03:00
# include "bmap.h"
# include "glock.h"
# include "inode.h"
# include "meta_io.h"
# include "quota.h"
# include "rgrp.h"
# include "trans.h"
2006-02-08 14:50:51 +03:00
# include "dir.h"
2006-02-28 01:23:27 +03:00
# include "util.h"
2009-06-12 11:49:20 +04:00
# include "trace_gfs2.h"
2006-01-16 19:50:04 +03:00
/* This doesn't need to be that large as max 64 bit pointers in a 4k
* block is 512 , so __u16 is fine for that . It saves stack space to
* keep it small .
*/
struct metapath {
2008-01-29 12:12:55 +03:00
struct buffer_head * mp_bh [ GFS2_MAX_META_HEIGHT ] ;
2006-01-16 19:50:04 +03:00
__u16 mp_list [ GFS2_MAX_META_HEIGHT ] ;
} ;
typedef int ( * block_call_t ) ( struct gfs2_inode * ip , struct buffer_head * dibh ,
2006-10-14 18:46:30 +04:00
struct buffer_head * bh , __be64 * top ,
__be64 * bottom , unsigned int height ,
2006-01-16 19:50:04 +03:00
void * data ) ;
struct strip_mine {
int sm_first ;
unsigned int sm_height ;
} ;
2006-07-26 18:51:20 +04:00
/**
* gfs2_unstuffer_page - unstuff a stuffed inode into a block cached by a page
* @ ip : the inode
* @ dibh : the dinode buffer
* @ block : the block number that was allocated
* @ private : any locked page held by the caller process
*
* Returns : errno
*/
static int gfs2_unstuffer_page ( struct gfs2_inode * ip , struct buffer_head * dibh ,
2006-09-04 20:49:07 +04:00
u64 block , struct page * page )
2006-07-26 18:51:20 +04:00
{
struct inode * inode = & ip - > i_inode ;
struct buffer_head * bh ;
int release = 0 ;
if ( ! page | | page - > index ) {
page = grab_cache_page ( inode - > i_mapping , 0 ) ;
if ( ! page )
return - ENOMEM ;
release = 1 ;
}
if ( ! PageUptodate ( page ) ) {
void * kaddr = kmap ( page ) ;
memcpy ( kaddr , dibh - > b_data + sizeof ( struct gfs2_dinode ) ,
2008-11-04 12:47:33 +03:00
ip - > i_disksize ) ;
memset ( kaddr + ip - > i_disksize , 0 ,
PAGE_CACHE_SIZE - ip - > i_disksize ) ;
2006-07-26 18:51:20 +04:00
kunmap ( page ) ;
SetPageUptodate ( page ) ;
}
if ( ! page_has_buffers ( page ) )
create_empty_buffers ( page , 1 < < inode - > i_blkbits ,
( 1 < < BH_Uptodate ) ) ;
bh = page_buffers ( page ) ;
if ( ! buffer_mapped ( bh ) )
map_bh ( bh , inode - > i_sb , block ) ;
set_buffer_uptodate ( bh ) ;
2007-08-27 12:49:37 +04:00
if ( ! gfs2_is_jdata ( ip ) )
mark_buffer_dirty ( bh ) ;
2007-10-17 11:35:19 +04:00
if ( ! gfs2_is_writeback ( ip ) )
2007-09-02 13:55:29 +04:00
gfs2_trans_add_bh ( ip - > i_gl , bh , 0 ) ;
2006-07-26 18:51:20 +04:00
if ( release ) {
unlock_page ( page ) ;
page_cache_release ( page ) ;
}
return 0 ;
}
2006-01-16 19:50:04 +03:00
/**
* gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big
* @ ip : The GFS2 inode to unstuff
* @ unstuffer : the routine that handles unstuffing a non - zero length file
* @ private : private data for the unstuffer
*
* This routine unstuffs a dinode and returns it to a " normal " state such
* that the height can be grown in the traditional way .
*
* Returns : errno
*/
2006-07-26 18:51:20 +04:00
int gfs2_unstuff_dinode ( struct gfs2_inode * ip , struct page * page )
2006-01-16 19:50:04 +03:00
{
struct buffer_head * bh , * dibh ;
2006-10-02 20:39:19 +04:00
struct gfs2_dinode * di ;
2006-09-04 20:49:07 +04:00
u64 block = 0 ;
2006-02-08 14:50:51 +03:00
int isdir = gfs2_is_dir ( ip ) ;
2006-01-16 19:50:04 +03:00
int error ;
down_write ( & ip - > i_rw_mutex ) ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto out ;
2006-09-25 17:26:04 +04:00
2008-11-04 12:47:33 +03:00
if ( ip - > i_disksize ) {
2006-01-16 19:50:04 +03:00
/* Get a free block, fill it with the stuffed data,
and write it out to disk */
2008-02-06 13:11:15 +03:00
unsigned int n = 1 ;
2009-05-20 13:48:47 +04:00
error = gfs2_alloc_block ( ip , & block , & n ) ;
if ( error )
goto out_brelse ;
2006-02-08 14:50:51 +03:00
if ( isdir ) {
2008-02-01 16:16:55 +03:00
gfs2_trans_add_unrevoke ( GFS2_SB ( & ip - > i_inode ) , block , 1 ) ;
2006-04-24 18:07:13 +04:00
error = gfs2_dir_get_new_buffer ( ip , block , & bh ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_brelse ;
2006-10-02 20:39:19 +04:00
gfs2_buffer_copy_tail ( bh , sizeof ( struct gfs2_meta_header ) ,
2006-01-16 19:50:04 +03:00
dibh , sizeof ( struct gfs2_dinode ) ) ;
brelse ( bh ) ;
} else {
2006-07-26 18:51:20 +04:00
error = gfs2_unstuffer_page ( ip , dibh , block , page ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_brelse ;
}
}
/* Set up the pointer to the new block */
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-10-02 20:39:19 +04:00
di = ( struct gfs2_dinode * ) dibh - > b_data ;
2006-01-16 19:50:04 +03:00
gfs2_buffer_clear_tail ( dibh , sizeof ( struct gfs2_dinode ) ) ;
2008-11-04 12:47:33 +03:00
if ( ip - > i_disksize ) {
2006-10-02 20:39:19 +04:00
* ( __be64 * ) ( di + 1 ) = cpu_to_be64 ( block ) ;
2008-02-12 17:17:27 +03:00
gfs2_add_inode_blocks ( & ip - > i_inode , 1 ) ;
di - > di_blocks = cpu_to_be64 ( gfs2_get_inode_blocks ( & ip - > i_inode ) ) ;
2006-01-16 19:50:04 +03:00
}
2008-01-28 13:37:35 +03:00
ip - > i_height = 1 ;
2006-10-02 20:39:19 +04:00
di - > di_height = cpu_to_be16 ( 1 ) ;
2006-01-16 19:50:04 +03:00
2006-09-04 20:04:26 +04:00
out_brelse :
2006-01-16 19:50:04 +03:00
brelse ( dibh ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
up_write ( & ip - > i_rw_mutex ) ;
return error ;
}
/**
* find_metapath - Find path through the metadata tree
2008-02-22 19:09:31 +03:00
* @ sdp : The superblock
2006-01-16 19:50:04 +03:00
* @ mp : The metapath to return the result in
* @ block : The disk block to look up
2008-02-22 19:09:31 +03:00
* @ height : The pre - calculated height of the metadata tree
2006-01-16 19:50:04 +03:00
*
* This routine returns a struct metapath structure that defines a path
* through the metadata of inode " ip " to get to block " block " .
*
* Example :
* Given : " ip " is a height 3 file , " offset " is 101342453 , and this is a
* filesystem with a blocksize of 4096.
*
* find_metapath ( ) would return a struct metapath structure set to :
* mp_offset = 101342453 , mp_height = 3 , mp_list [ 0 ] = 0 , mp_list [ 1 ] = 48 ,
* and mp_list [ 2 ] = 165.
*
* That means that in order to get to the block containing the byte at
* offset 101342453 , we would load the indirect block pointed to by pointer
* 0 in the dinode . We would then load the indirect block pointed to by
* pointer 48 in that indirect block . We would then load the data block
* pointed to by pointer 165 in that indirect block .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* | Dinode | |
* | | 4 |
* | | 0 1 2 3 4 5 9 |
* | | 6 |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* |
* |
* V
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* | Indirect Block |
* | 5 |
* | 4 4 4 4 4 5 5 1 |
* | 0 5 6 7 8 9 0 1 2 |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* |
* |
* V
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* | Indirect Block |
* | 1 1 1 1 1 5 |
* | 6 6 6 6 6 1 |
* | 0 3 4 5 6 7 2 |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* |
* |
* V
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* | Data block containing offset |
* | 101342453 |
* | |
* | |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
*/
2008-02-22 19:09:31 +03:00
static void find_metapath ( const struct gfs2_sbd * sdp , u64 block ,
struct metapath * mp , unsigned int height )
2006-01-16 19:50:04 +03:00
{
unsigned int i ;
2008-02-22 19:09:31 +03:00
for ( i = height ; i - - ; )
2008-01-28 20:24:35 +03:00
mp - > mp_list [ i ] = do_div ( block , sdp - > sd_inptrs ) ;
2006-01-16 19:50:04 +03:00
}
2008-06-24 21:53:38 +04:00
static inline unsigned int metapath_branch_start ( const struct metapath * mp )
2008-02-22 19:09:31 +03:00
{
2008-06-24 21:53:38 +04:00
if ( mp - > mp_list [ 0 ] = = 0 )
return 2 ;
return 1 ;
2008-02-22 19:09:31 +03:00
}
2006-01-16 19:50:04 +03:00
/**
* metapointer - Return pointer to start of metadata in a buffer
* @ height : The metadata height ( 0 = dinode )
* @ mp : The metapath
*
* Return a pointer to the block number of the next height of the metadata
* tree given a buffer containing the pointer to the current height of the
* metadata tree .
*/
2008-02-22 19:09:31 +03:00
static inline __be64 * metapointer ( unsigned int height , const struct metapath * mp )
2006-01-16 19:50:04 +03:00
{
2008-01-29 12:12:55 +03:00
struct buffer_head * bh = mp - > mp_bh [ height ] ;
2006-01-16 19:50:04 +03:00
unsigned int head_size = ( height > 0 ) ?
sizeof ( struct gfs2_meta_header ) : sizeof ( struct gfs2_dinode ) ;
2008-02-22 19:09:31 +03:00
return ( ( __be64 * ) ( bh - > b_data + head_size ) ) + mp - > mp_list [ height ] ;
2006-01-16 19:50:04 +03:00
}
/**
2008-02-22 19:09:31 +03:00
* lookup_metapath - Walk the metadata tree to a specific point
* @ ip : The inode
2006-01-16 19:50:04 +03:00
* @ mp : The metapath
*
2008-02-22 19:09:31 +03:00
* Assumes that the inode ' s buffer has already been looked up and
* hooked onto mp - > mp_bh [ 0 ] and that the metapath has been initialised
* by find_metapath ( ) .
*
* If this function encounters part of the tree which has not been
* allocated , it returns the current height of the tree at the point
* at which it found the unallocated block . Blocks which are found are
* added to the mp - > mp_bh [ ] list .
2006-01-16 19:50:04 +03:00
*
2008-02-22 19:09:31 +03:00
* Returns : error or height of metadata tree
2006-01-16 19:50:04 +03:00
*/
2008-02-22 19:09:31 +03:00
static int lookup_metapath ( struct gfs2_inode * ip , struct metapath * mp )
2008-01-28 18:10:29 +03:00
{
unsigned int end_of_metadata = ip - > i_height - 1 ;
unsigned int x ;
2008-02-22 19:09:31 +03:00
__be64 * ptr ;
u64 dblock ;
2008-02-12 17:48:39 +03:00
int ret ;
2008-01-28 18:10:29 +03:00
for ( x = 0 ; x < end_of_metadata ; x + + ) {
2008-02-22 19:09:31 +03:00
ptr = metapointer ( x , mp ) ;
dblock = be64_to_cpu ( * ptr ) ;
if ( ! dblock )
return x + 1 ;
2008-01-28 18:10:29 +03:00
2008-02-22 19:09:31 +03:00
ret = gfs2_meta_indirect_buffer ( ip , x + 1 , dblock , 0 , & mp - > mp_bh [ x + 1 ] ) ;
2008-01-28 18:10:29 +03:00
if ( ret )
return ret ;
}
2008-02-22 19:09:31 +03:00
return ip - > i_height ;
2008-01-29 12:12:55 +03:00
}
2008-02-22 19:09:31 +03:00
static inline void release_metapath ( struct metapath * mp )
2008-01-29 12:12:55 +03:00
{
int i ;
2008-02-22 19:09:31 +03:00
for ( i = 0 ; i < GFS2_MAX_META_HEIGHT ; i + + ) {
if ( mp - > mp_bh [ i ] = = NULL )
break ;
brelse ( mp - > mp_bh [ i ] ) ;
}
2008-01-28 18:10:29 +03:00
}
2008-02-08 16:18:11 +03:00
/**
* gfs2_extent_length - Returns length of an extent of blocks
* @ start : Start of the buffer
* @ len : Length of the buffer in bytes
* @ ptr : Current position in the buffer
* @ limit : Max extent length to return ( 0 = unlimited )
* @ eob : Set to 1 if we hit " end of block "
*
* If the first block is zero ( unallocated ) it will return the number of
* unallocated blocks in the extent , otherwise it will return the number
* of contiguous blocks in the extent .
*
* Returns : The length of the extent ( minimum of one block )
*/
static inline unsigned int gfs2_extent_length ( void * start , unsigned int len , __be64 * ptr , unsigned limit , int * eob )
{
const __be64 * end = ( start + len ) ;
const __be64 * first = ptr ;
u64 d = be64_to_cpu ( * ptr ) ;
* eob = 0 ;
do {
ptr + + ;
if ( ptr > = end )
break ;
if ( limit & & - - limit = = 0 )
break ;
if ( d )
d + + ;
} while ( be64_to_cpu ( * ptr ) = = d ) ;
if ( ptr > = end )
* eob = 1 ;
return ( ptr - first ) ;
}
2008-02-22 19:09:31 +03:00
static inline void bmap_lock ( struct gfs2_inode * ip , int create )
2006-11-15 23:21:06 +03:00
{
if ( create )
down_write ( & ip - > i_rw_mutex ) ;
else
down_read ( & ip - > i_rw_mutex ) ;
}
2008-02-22 19:09:31 +03:00
static inline void bmap_unlock ( struct gfs2_inode * ip , int create )
2006-11-15 23:21:06 +03:00
{
if ( create )
up_write ( & ip - > i_rw_mutex ) ;
else
up_read ( & ip - > i_rw_mutex ) ;
}
2008-02-22 19:09:31 +03:00
static inline __be64 * gfs2_indirect_init ( struct metapath * mp ,
struct gfs2_glock * gl , unsigned int i ,
unsigned offset , u64 bn )
{
__be64 * ptr = ( __be64 * ) ( mp - > mp_bh [ i - 1 ] - > b_data +
( ( i > 1 ) ? sizeof ( struct gfs2_meta_header ) :
sizeof ( struct gfs2_dinode ) ) ) ;
BUG_ON ( i < 1 ) ;
BUG_ON ( mp - > mp_bh [ i ] ! = NULL ) ;
mp - > mp_bh [ i ] = gfs2_meta_new ( gl , bn ) ;
gfs2_trans_add_bh ( gl , mp - > mp_bh [ i ] , 1 ) ;
gfs2_metatype_set ( mp - > mp_bh [ i ] , GFS2_METATYPE_IN , GFS2_FORMAT_IN ) ;
gfs2_buffer_clear_tail ( mp - > mp_bh [ i ] , sizeof ( struct gfs2_meta_header ) ) ;
ptr + = offset ;
* ptr = cpu_to_be64 ( bn ) ;
return ptr ;
}
enum alloc_state {
ALLOC_DATA = 0 ,
ALLOC_GROW_DEPTH = 1 ,
ALLOC_GROW_HEIGHT = 2 ,
/* ALLOC_UNSTUFF = 3, TBD and rather complicated */
} ;
/**
* gfs2_bmap_alloc - Build a metadata tree of the requested height
* @ inode : The GFS2 inode
* @ lblock : The logical starting block of the extent
* @ bh_map : This is used to return the mapping details
* @ mp : The metapath
* @ sheight : The starting height ( i . e . whats already mapped )
* @ height : The height to build to
* @ maxlen : The max number of data blocks to alloc
*
* In this routine we may have to alloc :
* i ) Indirect blocks to grow the metadata tree height
* ii ) Indirect blocks to fill in lower part of the metadata tree
* iii ) Data blocks
*
* The function is in two parts . The first part works out the total
* number of blocks which we need . The second part does the actual
* allocation asking for an extent at a time ( if enough contiguous free
* blocks are available , there will only be one request per bmap call )
* and uses the state machine to initialise the blocks in order .
*
* Returns : errno on error
*/
static int gfs2_bmap_alloc ( struct inode * inode , const sector_t lblock ,
struct buffer_head * bh_map , struct metapath * mp ,
const unsigned int sheight ,
const unsigned int height ,
const unsigned int maxlen )
{
struct gfs2_inode * ip = GFS2_I ( inode ) ;
struct gfs2_sbd * sdp = GFS2_SB ( inode ) ;
struct buffer_head * dibh = mp - > mp_bh [ 0 ] ;
u64 bn , dblock = 0 ;
2008-06-24 21:53:38 +04:00
unsigned n , i , blks , alloced = 0 , iblks = 0 , branch_start = 0 ;
2008-02-22 19:09:31 +03:00
unsigned dblks = 0 ;
unsigned ptrs_per_blk ;
const unsigned end_of_metadata = height - 1 ;
int eob = 0 ;
enum alloc_state state ;
__be64 * ptr ;
__be64 zero_bn = 0 ;
BUG_ON ( sheight < 1 ) ;
BUG_ON ( dibh = = NULL ) ;
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
if ( height = = sheight ) {
struct buffer_head * bh ;
/* Bottom indirect block exists, find unalloced extent size */
ptr = metapointer ( end_of_metadata , mp ) ;
bh = mp - > mp_bh [ end_of_metadata ] ;
dblks = gfs2_extent_length ( bh - > b_data , bh - > b_size , ptr , maxlen ,
& eob ) ;
BUG_ON ( dblks < 1 ) ;
state = ALLOC_DATA ;
} else {
/* Need to allocate indirect blocks */
ptrs_per_blk = height > 1 ? sdp - > sd_inptrs : sdp - > sd_diptrs ;
dblks = min ( maxlen , ptrs_per_blk - mp - > mp_list [ end_of_metadata ] ) ;
if ( height = = ip - > i_height ) {
/* Writing into existing tree, extend tree down */
iblks = height - sheight ;
state = ALLOC_GROW_DEPTH ;
} else {
/* Building up tree height */
state = ALLOC_GROW_HEIGHT ;
iblks = height - ip - > i_height ;
2008-06-24 21:53:38 +04:00
branch_start = metapath_branch_start ( mp ) ;
iblks + = ( height - branch_start ) ;
2008-02-22 19:09:31 +03:00
}
}
/* start of the second part of the function (state machine) */
blks = dblks + iblks ;
i = sheight ;
do {
2009-05-20 13:48:47 +04:00
int error ;
2008-02-22 19:09:31 +03:00
n = blks - alloced ;
2009-05-20 13:48:47 +04:00
error = gfs2_alloc_block ( ip , & bn , & n ) ;
if ( error )
return error ;
2008-02-22 19:09:31 +03:00
alloced + = n ;
if ( state ! = ALLOC_DATA | | gfs2_is_jdata ( ip ) )
gfs2_trans_add_unrevoke ( sdp , bn , n ) ;
switch ( state ) {
/* Growing height of tree */
case ALLOC_GROW_HEIGHT :
if ( i = = 1 ) {
ptr = ( __be64 * ) ( dibh - > b_data +
sizeof ( struct gfs2_dinode ) ) ;
zero_bn = * ptr ;
}
for ( ; i - 1 < height - ip - > i_height & & n > 0 ; i + + , n - - )
gfs2_indirect_init ( mp , ip - > i_gl , i , 0 , bn + + ) ;
if ( i - 1 = = height - ip - > i_height ) {
i - - ;
gfs2_buffer_copy_tail ( mp - > mp_bh [ i ] ,
sizeof ( struct gfs2_meta_header ) ,
dibh , sizeof ( struct gfs2_dinode ) ) ;
gfs2_buffer_clear_tail ( dibh ,
sizeof ( struct gfs2_dinode ) +
sizeof ( __be64 ) ) ;
ptr = ( __be64 * ) ( mp - > mp_bh [ i ] - > b_data +
sizeof ( struct gfs2_meta_header ) ) ;
* ptr = zero_bn ;
state = ALLOC_GROW_DEPTH ;
2008-06-24 21:53:38 +04:00
for ( i = branch_start ; i < height ; i + + ) {
2008-02-22 19:09:31 +03:00
if ( mp - > mp_bh [ i ] = = NULL )
break ;
brelse ( mp - > mp_bh [ i ] ) ;
mp - > mp_bh [ i ] = NULL ;
}
2008-06-24 21:53:38 +04:00
i = branch_start ;
2008-02-22 19:09:31 +03:00
}
if ( n = = 0 )
break ;
/* Branching from existing tree */
case ALLOC_GROW_DEPTH :
if ( i > 1 & & i < height )
gfs2_trans_add_bh ( ip - > i_gl , mp - > mp_bh [ i - 1 ] , 1 ) ;
for ( ; i < height & & n > 0 ; i + + , n - - )
gfs2_indirect_init ( mp , ip - > i_gl , i ,
mp - > mp_list [ i - 1 ] , bn + + ) ;
if ( i = = height )
state = ALLOC_DATA ;
if ( n = = 0 )
break ;
/* Tree complete, adding data blocks */
case ALLOC_DATA :
BUG_ON ( n > dblks ) ;
BUG_ON ( mp - > mp_bh [ end_of_metadata ] = = NULL ) ;
gfs2_trans_add_bh ( ip - > i_gl , mp - > mp_bh [ end_of_metadata ] , 1 ) ;
dblks = n ;
ptr = metapointer ( end_of_metadata , mp ) ;
dblock = bn ;
while ( n - - > 0 )
* ptr + + = cpu_to_be64 ( bn + + ) ;
break ;
}
2010-02-12 13:10:55 +03:00
} while ( ( state ! = ALLOC_DATA ) | | ! dblock ) ;
2008-02-22 19:09:31 +03:00
ip - > i_height = height ;
gfs2_add_inode_blocks ( & ip - > i_inode , alloced ) ;
gfs2_dinode_out ( ip , mp - > mp_bh [ 0 ] - > b_data ) ;
map_bh ( bh_map , inode - > i_sb , dblock ) ;
bh_map - > b_size = dblks < < inode - > i_blkbits ;
set_buffer_new ( bh_map ) ;
return 0 ;
}
2006-01-16 19:50:04 +03:00
/**
2006-11-15 23:21:06 +03:00
* gfs2_block_map - Map a block from an inode to a disk block
2006-05-06 00:59:11 +04:00
* @ inode : The inode
2006-01-16 19:50:04 +03:00
* @ lblock : The logical block number
2006-11-15 23:21:06 +03:00
* @ bh_map : The bh to be mapped
2008-02-22 19:09:31 +03:00
* @ create : True if its ok to alloc blocks to satify the request
2006-01-16 19:50:04 +03:00
*
2008-02-22 19:09:31 +03:00
* Sets buffer_mapped ( ) if successful , sets buffer_boundary ( ) if a
* read of metadata will be required before the next block can be
* mapped . Sets buffer_new ( ) if new blocks were allocated .
2006-01-16 19:50:04 +03:00
*
* Returns : errno
*/
2007-12-10 23:13:27 +03:00
int gfs2_block_map ( struct inode * inode , sector_t lblock ,
struct buffer_head * bh_map , int create )
2006-01-16 19:50:04 +03:00
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
struct gfs2_sbd * sdp = GFS2_SB ( inode ) ;
2008-01-28 13:37:35 +03:00
unsigned int bsize = sdp - > sd_sb . sb_bsize ;
2008-02-22 19:09:31 +03:00
const unsigned int maxlen = bh_map - > b_size > > inode - > i_blkbits ;
2008-01-28 13:37:35 +03:00
const u64 * arr = sdp - > sd_heightsize ;
2008-02-22 19:09:31 +03:00
__be64 * ptr ;
u64 size ;
struct metapath mp ;
int ret ;
int eob ;
unsigned int len ;
struct buffer_head * bh ;
u8 height ;
2006-09-22 01:05:23 +04:00
2008-02-22 19:09:31 +03:00
BUG_ON ( maxlen = = 0 ) ;
2006-01-16 19:50:04 +03:00
2008-01-29 12:12:55 +03:00
memset ( mp . mp_bh , 0 , sizeof ( mp . mp_bh ) ) ;
2008-02-22 19:09:31 +03:00
bmap_lock ( ip , create ) ;
2006-11-15 23:21:06 +03:00
clear_buffer_mapped ( bh_map ) ;
clear_buffer_new ( bh_map ) ;
clear_buffer_boundary ( bh_map ) ;
2009-06-12 11:49:20 +04:00
trace_gfs2_bmap ( ip , bh_map , lblock , create , 1 ) ;
2008-01-28 13:37:35 +03:00
if ( gfs2_is_dir ( ip ) ) {
bsize = sdp - > sd_jbsize ;
arr = sdp - > sd_jheightsize ;
}
2006-11-15 23:21:06 +03:00
2008-02-22 19:09:31 +03:00
ret = gfs2_meta_inode_buffer ( ip , & mp . mp_bh [ 0 ] ) ;
if ( ret )
goto out ;
2006-01-16 19:50:04 +03:00
2008-02-22 19:09:31 +03:00
height = ip - > i_height ;
size = ( lblock + 1 ) * bsize ;
while ( size > arr [ height ] )
height + + ;
find_metapath ( sdp , lblock , & mp , height ) ;
ret = 1 ;
if ( height > ip - > i_height | | gfs2_is_stuffed ( ip ) )
goto do_alloc ;
ret = lookup_metapath ( ip , & mp ) ;
if ( ret < 0 )
goto out ;
if ( ret ! = ip - > i_height )
goto do_alloc ;
ptr = metapointer ( ip - > i_height - 1 , & mp ) ;
if ( * ptr = = 0 )
goto do_alloc ;
map_bh ( bh_map , inode - > i_sb , be64_to_cpu ( * ptr ) ) ;
bh = mp . mp_bh [ ip - > i_height - 1 ] ;
len = gfs2_extent_length ( bh - > b_data , bh - > b_size , ptr , maxlen , & eob ) ;
bh_map - > b_size = ( len < < inode - > i_blkbits ) ;
if ( eob )
set_buffer_boundary ( bh_map ) ;
ret = 0 ;
out :
release_metapath ( & mp ) ;
2009-06-12 11:49:20 +04:00
trace_gfs2_bmap ( ip , bh_map , lblock , create , ret ) ;
2008-02-22 19:09:31 +03:00
bmap_unlock ( ip , create ) ;
return ret ;
2008-02-08 16:18:11 +03:00
2008-02-22 19:09:31 +03:00
do_alloc :
/* All allocations are done here, firstly check create flag */
if ( ! create ) {
BUG_ON ( gfs2_is_stuffed ( ip ) ) ;
ret = 0 ;
goto out ;
2006-01-16 19:50:04 +03:00
}
2008-02-22 19:09:31 +03:00
/* At this point ret is the tree depth of already allocated blocks */
ret = gfs2_bmap_alloc ( inode , lblock , bh_map , & mp , ret , height , maxlen ) ;
goto out ;
2006-05-06 00:59:11 +04:00
}
2008-01-28 11:47:38 +03:00
/*
* Deprecated : do not use in new code
*/
2006-05-06 00:59:11 +04:00
int gfs2_extent_map ( struct inode * inode , u64 lblock , int * new , u64 * dblock , unsigned * extlen )
{
2006-10-14 01:25:45 +04:00
struct buffer_head bh = { . b_state = 0 , . b_blocknr = 0 } ;
2006-09-19 01:18:23 +04:00
int ret ;
2006-05-06 00:59:11 +04:00
int create = * new ;
BUG_ON ( ! extlen ) ;
BUG_ON ( ! dblock ) ;
BUG_ON ( ! new ) ;
2008-02-22 19:09:31 +03:00
bh . b_size = 1 < < ( inode - > i_blkbits + ( create ? 0 : 5 ) ) ;
2007-12-10 23:13:27 +03:00
ret = gfs2_block_map ( inode , lblock , & bh , create ) ;
2006-09-19 01:18:23 +04:00
* extlen = bh . b_size > > inode - > i_blkbits ;
* dblock = bh . b_blocknr ;
if ( buffer_new ( & bh ) )
* new = 1 ;
else
* new = 0 ;
return ret ;
2006-01-16 19:50:04 +03:00
}
/**
* recursive_scan - recursively scan through the end of a file
* @ ip : the inode
* @ dibh : the dinode buffer
* @ mp : the path through the metadata to the point to start
* @ height : the height the recursion is at
* @ block : the indirect block to look at
* @ first : 1 if this is the first block
* @ bc : the call to make for each piece of metadata
* @ data : data opaque to this function to pass to @ bc
*
* When this is first called @ height and @ block should be zero and
* @ first should be 1.
*
* Returns : errno
*/
static int recursive_scan ( struct gfs2_inode * ip , struct buffer_head * dibh ,
struct metapath * mp , unsigned int height ,
2006-09-04 20:49:07 +04:00
u64 block , int first , block_call_t bc ,
2006-01-16 19:50:04 +03:00
void * data )
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
struct buffer_head * bh = NULL ;
2006-10-14 18:46:30 +04:00
__be64 * top , * bottom ;
2006-09-04 20:49:07 +04:00
u64 bn ;
2006-01-16 19:50:04 +03:00
int error ;
int mh_size = sizeof ( struct gfs2_meta_header ) ;
if ( ! height ) {
error = gfs2_meta_inode_buffer ( ip , & bh ) ;
if ( error )
return error ;
dibh = bh ;
2006-10-14 18:46:30 +04:00
top = ( __be64 * ) ( bh - > b_data + sizeof ( struct gfs2_dinode ) ) + mp - > mp_list [ 0 ] ;
bottom = ( __be64 * ) ( bh - > b_data + sizeof ( struct gfs2_dinode ) ) + sdp - > sd_diptrs ;
2006-01-16 19:50:04 +03:00
} else {
error = gfs2_meta_indirect_buffer ( ip , height , block , 0 , & bh ) ;
if ( error )
return error ;
2006-10-14 18:46:30 +04:00
top = ( __be64 * ) ( bh - > b_data + mh_size ) +
2006-09-05 16:30:40 +04:00
( first ? mp - > mp_list [ height ] : 0 ) ;
2006-01-16 19:50:04 +03:00
2006-10-14 18:46:30 +04:00
bottom = ( __be64 * ) ( bh - > b_data + mh_size ) + sdp - > sd_inptrs ;
2006-01-16 19:50:04 +03:00
}
error = bc ( ip , dibh , bh , top , bottom , height , data ) ;
if ( error )
goto out ;
2008-01-28 13:37:35 +03:00
if ( height < ip - > i_height - 1 )
2006-01-16 19:50:04 +03:00
for ( ; top < bottom ; top + + , first = 0 ) {
if ( ! * top )
continue ;
bn = be64_to_cpu ( * top ) ;
error = recursive_scan ( ip , dibh , mp , height + 1 , bn ,
first , bc , data ) ;
if ( error )
break ;
}
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
brelse ( bh ) ;
return error ;
}
/**
* do_strip - Look for a layer a particular layer of the file and strip it off
* @ ip : the inode
* @ dibh : the dinode buffer
* @ bh : A buffer of pointers
* @ top : The first pointer in the buffer
* @ bottom : One more than the last pointer
* @ height : the height this buffer is at
* @ data : a pointer to a struct strip_mine
*
* Returns : errno
*/
static int do_strip ( struct gfs2_inode * ip , struct buffer_head * dibh ,
2006-10-14 18:46:30 +04:00
struct buffer_head * bh , __be64 * top , __be64 * bottom ,
2006-01-16 19:50:04 +03:00
unsigned int height , void * data )
{
2006-06-14 23:32:57 +04:00
struct strip_mine * sm = data ;
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_rgrp_list rlist ;
2006-09-04 20:49:07 +04:00
u64 bn , bstart ;
u32 blen ;
2006-10-14 18:46:30 +04:00
__be64 * p ;
2006-01-16 19:50:04 +03:00
unsigned int rg_blocks = 0 ;
int metadata ;
unsigned int revokes = 0 ;
int x ;
int error ;
if ( ! * top )
sm - > sm_first = 0 ;
if ( height ! = sm - > sm_height )
return 0 ;
if ( sm - > sm_first ) {
top + + ;
sm - > sm_first = 0 ;
}
2008-01-28 13:37:35 +03:00
metadata = ( height ! = ip - > i_height - 1 ) ;
2006-01-16 19:50:04 +03:00
if ( metadata )
revokes = ( height ) ? sdp - > sd_inptrs : sdp - > sd_diptrs ;
2008-01-10 18:18:55 +03:00
error = gfs2_rindex_hold ( sdp , & ip - > i_alloc - > al_ri_gh ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
memset ( & rlist , 0 , sizeof ( struct gfs2_rgrp_list ) ) ;
bstart = 0 ;
blen = 0 ;
for ( p = top ; p < bottom ; p + + ) {
if ( ! * p )
continue ;
bn = be64_to_cpu ( * p ) ;
if ( bstart + blen = = bn )
blen + + ;
else {
if ( bstart )
gfs2_rlist_add ( sdp , & rlist , bstart ) ;
bstart = bn ;
blen = 1 ;
}
}
if ( bstart )
gfs2_rlist_add ( sdp , & rlist , bstart ) ;
else
goto out ; /* Nothing to do */
2008-01-28 20:13:02 +03:00
gfs2_rlist_alloc ( & rlist , LM_ST_EXCLUSIVE ) ;
2006-01-16 19:50:04 +03:00
for ( x = 0 ; x < rlist . rl_rgrps ; x + + ) {
struct gfs2_rgrpd * rgd ;
2006-02-28 01:23:27 +03:00
rgd = rlist . rl_ghs [ x ] . gh_gl - > gl_object ;
2007-06-01 17:11:58 +04:00
rg_blocks + = rgd - > rd_length ;
2006-01-16 19:50:04 +03:00
}
error = gfs2_glock_nq_m ( rlist . rl_rgrps , rlist . rl_ghs ) ;
if ( error )
goto out_rlist ;
error = gfs2_trans_begin ( sdp , rg_blocks + RES_DINODE +
RES_INDIRECT + RES_STATFS + RES_QUOTA ,
revokes ) ;
if ( error )
goto out_rg_gunlock ;
down_write ( & ip - > i_rw_mutex ) ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
gfs2_trans_add_bh ( ip - > i_gl , bh , 1 ) ;
2006-01-16 19:50:04 +03:00
bstart = 0 ;
blen = 0 ;
for ( p = top ; p < bottom ; p + + ) {
if ( ! * p )
continue ;
bn = be64_to_cpu ( * p ) ;
if ( bstart + blen = = bn )
blen + + ;
else {
if ( bstart ) {
if ( metadata )
gfs2_free_meta ( ip , bstart , blen ) ;
else
gfs2_free_data ( ip , bstart , blen ) ;
}
bstart = bn ;
blen = 1 ;
}
* p = 0 ;
2008-02-12 17:17:27 +03:00
gfs2_add_inode_blocks ( & ip - > i_inode , - 1 ) ;
2006-01-16 19:50:04 +03:00
}
if ( bstart ) {
if ( metadata )
gfs2_free_meta ( ip , bstart , blen ) ;
else
gfs2_free_data ( ip , bstart , blen ) ;
}
2007-06-05 12:39:18 +04:00
ip - > i_inode . i_mtime = ip - > i_inode . i_ctime = CURRENT_TIME ;
2006-01-16 19:50:04 +03:00
2006-10-31 23:07:05 +03:00
gfs2_dinode_out ( ip , dibh - > b_data ) ;
2006-01-16 19:50:04 +03:00
up_write ( & ip - > i_rw_mutex ) ;
gfs2_trans_end ( sdp ) ;
2006-09-04 20:04:26 +04:00
out_rg_gunlock :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_m ( rlist . rl_rgrps , rlist . rl_ghs ) ;
2006-09-04 20:04:26 +04:00
out_rlist :
2006-01-16 19:50:04 +03:00
gfs2_rlist_free ( & rlist ) ;
2006-09-04 20:04:26 +04:00
out :
2008-01-10 18:18:55 +03:00
gfs2_glock_dq_uninit ( & ip - > i_alloc - > al_ri_gh ) ;
2006-01-16 19:50:04 +03:00
return error ;
}
/**
* do_grow - Make a file look bigger than it is
* @ ip : the inode
* @ size : the size to set the file to
*
* Called with an exclusive lock on @ ip .
*
* Returns : errno
*/
2006-09-04 20:49:07 +04:00
static int do_grow ( struct gfs2_inode * ip , u64 size )
2006-01-16 19:50:04 +03:00
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_alloc * al ;
struct buffer_head * dibh ;
int error ;
al = gfs2_alloc_get ( ip ) ;
2008-03-03 21:54:21 +03:00
if ( ! al )
return - ENOMEM ;
2006-01-16 19:50:04 +03:00
2008-03-10 18:34:50 +03:00
error = gfs2_quota_lock_check ( ip ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out ;
al - > al_requested = sdp - > sd_max_height + RES_DATA ;
error = gfs2_inplace_reserve ( ip ) ;
if ( error )
goto out_gunlock_q ;
error = gfs2_trans_begin ( sdp ,
2007-06-01 17:11:58 +04:00
sdp - > sd_max_height + al - > al_rgd - > rd_length +
2006-01-16 19:50:04 +03:00
RES_JDATA + RES_DINODE + RES_STATFS + RES_QUOTA , 0 ) ;
if ( error )
goto out_ipres ;
2008-02-22 19:09:31 +03:00
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto out_end_trans ;
2006-01-16 19:50:04 +03:00
if ( size > sdp - > sd_sb . sb_bsize - sizeof ( struct gfs2_dinode ) ) {
if ( gfs2_is_stuffed ( ip ) ) {
2006-07-26 18:51:20 +04:00
error = gfs2_unstuff_dinode ( ip , NULL ) ;
2006-01-16 19:50:04 +03:00
if ( error )
2008-02-22 19:09:31 +03:00
goto out_brelse ;
2006-01-16 19:50:04 +03:00
}
}
2008-11-04 12:47:33 +03:00
ip - > i_disksize = size ;
2007-06-05 12:39:18 +04:00
ip - > i_inode . i_mtime = ip - > i_inode . i_ctime = CURRENT_TIME ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-10-31 23:07:05 +03:00
gfs2_dinode_out ( ip , dibh - > b_data ) ;
2006-01-16 19:50:04 +03:00
2008-02-22 19:09:31 +03:00
out_brelse :
brelse ( dibh ) ;
2006-09-04 20:04:26 +04:00
out_end_trans :
2006-01-16 19:50:04 +03:00
gfs2_trans_end ( sdp ) ;
2006-09-04 20:04:26 +04:00
out_ipres :
2006-01-16 19:50:04 +03:00
gfs2_inplace_release ( ip ) ;
2006-09-04 20:04:26 +04:00
out_gunlock_q :
2006-01-16 19:50:04 +03:00
gfs2_quota_unlock ( ip ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
gfs2_alloc_put ( ip ) ;
return error ;
}
2006-07-26 19:27:10 +04:00
/**
* gfs2_block_truncate_page - Deal with zeroing out data for truncate
*
* This is partly borrowed from ext3 .
*/
static int gfs2_block_truncate_page ( struct address_space * mapping )
{
struct inode * inode = mapping - > host ;
struct gfs2_inode * ip = GFS2_I ( inode ) ;
loff_t from = inode - > i_size ;
unsigned long index = from > > PAGE_CACHE_SHIFT ;
unsigned offset = from & ( PAGE_CACHE_SIZE - 1 ) ;
unsigned blocksize , iblock , length , pos ;
struct buffer_head * bh ;
struct page * page ;
int err ;
page = grab_cache_page ( mapping , index ) ;
if ( ! page )
return 0 ;
blocksize = inode - > i_sb - > s_blocksize ;
length = blocksize - ( offset & ( blocksize - 1 ) ) ;
iblock = index < < ( PAGE_CACHE_SHIFT - inode - > i_sb - > s_blocksize_bits ) ;
if ( ! page_has_buffers ( page ) )
create_empty_buffers ( page , blocksize , 0 ) ;
/* Find the buffer that contains "offset" */
bh = page_buffers ( page ) ;
pos = blocksize ;
while ( offset > = pos ) {
bh = bh - > b_this_page ;
iblock + + ;
pos + = blocksize ;
}
err = 0 ;
if ( ! buffer_mapped ( bh ) ) {
2007-12-10 23:13:27 +03:00
gfs2_block_map ( inode , iblock , bh , 0 ) ;
2006-07-26 19:27:10 +04:00
/* unmapped? It's a hole - nothing to do */
if ( ! buffer_mapped ( bh ) )
goto unlock ;
}
/* Ok, it's mapped. Make sure it's up-to-date */
if ( PageUptodate ( page ) )
set_buffer_uptodate ( bh ) ;
if ( ! buffer_uptodate ( bh ) ) {
err = - EIO ;
ll_rw_block ( READ , 1 , & bh ) ;
wait_on_buffer ( bh ) ;
/* Uhhuh. Read error. Complain and punt. */
if ( ! buffer_uptodate ( bh ) )
goto unlock ;
2007-06-26 05:14:31 +04:00
err = 0 ;
2006-07-26 19:27:10 +04:00
}
2007-10-17 11:35:19 +04:00
if ( ! gfs2_is_writeback ( ip ) )
2006-07-26 19:27:10 +04:00
gfs2_trans_add_bh ( ip - > i_gl , bh , 0 ) ;
2008-02-05 09:28:29 +03:00
zero_user ( page , offset , length ) ;
2009-06-10 12:09:40 +04:00
mark_buffer_dirty ( bh ) ;
2006-07-26 19:27:10 +04:00
unlock :
unlock_page ( page ) ;
page_cache_release ( page ) ;
return err ;
}
2006-09-04 20:49:07 +04:00
static int trunc_start ( struct gfs2_inode * ip , u64 size )
2006-01-16 19:50:04 +03:00
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
struct buffer_head * dibh ;
int journaled = gfs2_is_jdata ( ip ) ;
int error ;
error = gfs2_trans_begin ( sdp ,
2006-09-05 16:30:40 +04:00
RES_DINODE + ( journaled ? RES_JDATA : 0 ) , 0 ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto out ;
if ( gfs2_is_stuffed ( ip ) ) {
2008-11-04 12:47:33 +03:00
ip - > i_disksize = size ;
2007-06-05 12:39:18 +04:00
ip - > i_inode . i_mtime = ip - > i_inode . i_ctime = CURRENT_TIME ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-10-31 23:07:05 +03:00
gfs2_dinode_out ( ip , dibh - > b_data ) ;
2006-01-16 19:50:04 +03:00
gfs2_buffer_clear_tail ( dibh , sizeof ( struct gfs2_dinode ) + size ) ;
error = 1 ;
} else {
2006-09-04 20:49:07 +04:00
if ( size & ( u64 ) ( sdp - > sd_sb . sb_bsize - 1 ) )
2006-06-14 23:32:57 +04:00
error = gfs2_block_truncate_page ( ip - > i_inode . i_mapping ) ;
2006-01-16 19:50:04 +03:00
if ( ! error ) {
2008-11-04 12:47:33 +03:00
ip - > i_disksize = size ;
2007-06-05 12:39:18 +04:00
ip - > i_inode . i_mtime = ip - > i_inode . i_ctime = CURRENT_TIME ;
2008-11-04 13:05:22 +03:00
ip - > i_diskflags | = GFS2_DIF_TRUNC_IN_PROG ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-10-31 23:07:05 +03:00
gfs2_dinode_out ( ip , dibh - > b_data ) ;
2006-01-16 19:50:04 +03:00
}
}
brelse ( dibh ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
gfs2_trans_end ( sdp ) ;
return error ;
}
2006-09-04 20:49:07 +04:00
static int trunc_dealloc ( struct gfs2_inode * ip , u64 size )
2006-01-16 19:50:04 +03:00
{
2008-02-22 19:09:31 +03:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2008-01-28 13:37:35 +03:00
unsigned int height = ip - > i_height ;
2006-09-04 20:49:07 +04:00
u64 lblock ;
2006-01-16 19:50:04 +03:00
struct metapath mp ;
int error ;
if ( ! size )
lblock = 0 ;
2006-02-08 14:50:51 +03:00
else
2008-02-22 19:09:31 +03:00
lblock = ( size - 1 ) > > sdp - > sd_sb . sb_bsize_shift ;
2006-01-16 19:50:04 +03:00
2008-02-22 19:09:31 +03:00
find_metapath ( sdp , lblock , & mp , ip - > i_height ) ;
2008-03-03 21:54:21 +03:00
if ( ! gfs2_alloc_get ( ip ) )
return - ENOMEM ;
2006-01-16 19:50:04 +03:00
error = gfs2_quota_hold ( ip , NO_QUOTA_CHANGE , NO_QUOTA_CHANGE ) ;
if ( error )
goto out ;
while ( height - - ) {
struct strip_mine sm ;
sm . sm_first = ! ! size ;
sm . sm_height = height ;
error = recursive_scan ( ip , NULL , & mp , 0 , 0 , 1 , do_strip , & sm ) ;
if ( error )
break ;
}
gfs2_quota_unhold ( ip ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
gfs2_alloc_put ( ip ) ;
return error ;
}
static int trunc_end ( struct gfs2_inode * ip )
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
struct buffer_head * dibh ;
int error ;
error = gfs2_trans_begin ( sdp , RES_DINODE , 0 ) ;
if ( error )
return error ;
down_write ( & ip - > i_rw_mutex ) ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto out ;
2008-11-04 12:47:33 +03:00
if ( ! ip - > i_disksize ) {
2008-01-28 13:37:35 +03:00
ip - > i_height = 0 ;
2008-02-06 12:25:45 +03:00
ip - > i_goal = ip - > i_no_addr ;
2006-01-16 19:50:04 +03:00
gfs2_buffer_clear_tail ( dibh , sizeof ( struct gfs2_dinode ) ) ;
}
2007-06-05 12:39:18 +04:00
ip - > i_inode . i_mtime = ip - > i_inode . i_ctime = CURRENT_TIME ;
2008-11-04 13:05:22 +03:00
ip - > i_diskflags & = ~ GFS2_DIF_TRUNC_IN_PROG ;
2006-01-16 19:50:04 +03:00
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
2006-10-31 23:07:05 +03:00
gfs2_dinode_out ( ip , dibh - > b_data ) ;
2006-01-16 19:50:04 +03:00
brelse ( dibh ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
up_write ( & ip - > i_rw_mutex ) ;
gfs2_trans_end ( sdp ) ;
return error ;
}
/**
* do_shrink - make a file smaller
* @ ip : the inode
* @ size : the size to make the file
* @ truncator : function to truncate the last partial block
*
* Called with an exclusive lock on @ ip .
*
* Returns : errno
*/
2006-09-04 20:49:07 +04:00
static int do_shrink ( struct gfs2_inode * ip , u64 size )
2006-01-16 19:50:04 +03:00
{
int error ;
2006-01-24 13:37:06 +03:00
error = trunc_start ( ip , size ) ;
2006-01-16 19:50:04 +03:00
if ( error < 0 )
return error ;
if ( error > 0 )
return 0 ;
error = trunc_dealloc ( ip , size ) ;
if ( ! error )
error = trunc_end ( ip ) ;
return error ;
}
2007-08-20 17:29:53 +04:00
static int do_touch ( struct gfs2_inode * ip , u64 size )
{
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
struct buffer_head * dibh ;
int error ;
error = gfs2_trans_begin ( sdp , RES_DINODE , 0 ) ;
if ( error )
return error ;
down_write ( & ip - > i_rw_mutex ) ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto do_touch_out ;
ip - > i_inode . i_mtime = ip - > i_inode . i_ctime = CURRENT_TIME ;
gfs2_trans_add_bh ( ip - > i_gl , dibh , 1 ) ;
gfs2_dinode_out ( ip , dibh - > b_data ) ;
brelse ( dibh ) ;
do_touch_out :
up_write ( & ip - > i_rw_mutex ) ;
gfs2_trans_end ( sdp ) ;
return error ;
}
2006-01-16 19:50:04 +03:00
/**
2006-01-18 13:29:04 +03:00
* gfs2_truncatei - make a file a given size
2006-01-16 19:50:04 +03:00
* @ ip : the inode
* @ size : the size to make the file
* @ truncator : function to truncate the last partial block
*
* The file size can grow , shrink , or stay the same size .
*
* Returns : errno
*/
2006-09-04 20:49:07 +04:00
int gfs2_truncatei ( struct gfs2_inode * ip , u64 size )
2006-01-16 19:50:04 +03:00
{
int error ;
2006-11-01 20:22:46 +03:00
if ( gfs2_assert_warn ( GFS2_SB ( & ip - > i_inode ) , S_ISREG ( ip - > i_inode . i_mode ) ) )
2006-01-16 19:50:04 +03:00
return - EINVAL ;
2008-11-04 12:47:33 +03:00
if ( size > ip - > i_disksize )
2006-01-16 19:50:04 +03:00
error = do_grow ( ip , size ) ;
2008-11-04 12:47:33 +03:00
else if ( size < ip - > i_disksize )
2006-01-24 13:37:06 +03:00
error = do_shrink ( ip , size ) ;
2007-08-20 17:29:53 +04:00
else
/* update time stamps */
error = do_touch ( ip , size ) ;
2006-01-16 19:50:04 +03:00
return error ;
}
int gfs2_truncatei_resume ( struct gfs2_inode * ip )
{
int error ;
2008-11-04 12:47:33 +03:00
error = trunc_dealloc ( ip , ip - > i_disksize ) ;
2006-01-16 19:50:04 +03:00
if ( ! error )
error = trunc_end ( ip ) ;
return error ;
}
int gfs2_file_dealloc ( struct gfs2_inode * ip )
{
return trunc_dealloc ( ip , 0 ) ;
}
/**
* gfs2_write_alloc_required - figure out if a write will require an allocation
* @ ip : the file being written to
* @ offset : the offset to write to
* @ len : the number of bytes being written
* @ alloc_required : set to 1 if an alloc is required , 0 otherwise
*
* Returns : errno
*/
2006-09-04 20:49:07 +04:00
int gfs2_write_alloc_required ( struct gfs2_inode * ip , u64 offset ,
2006-01-16 19:50:04 +03:00
unsigned int len , int * alloc_required )
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2008-01-28 11:47:38 +03:00
struct buffer_head bh ;
unsigned int shift ;
u64 lblock , lblock_stop , size ;
2008-12-10 13:28:10 +03:00
u64 end_of_file ;
2006-01-16 19:50:04 +03:00
* alloc_required = 0 ;
if ( ! len )
return 0 ;
if ( gfs2_is_stuffed ( ip ) ) {
if ( offset + len >
sdp - > sd_sb . sb_bsize - sizeof ( struct gfs2_dinode ) )
* alloc_required = 1 ;
return 0 ;
}
2008-01-28 11:47:38 +03:00
* alloc_required = 1 ;
shift = sdp - > sd_sb . sb_bsize_shift ;
2008-12-10 13:28:10 +03:00
BUG_ON ( gfs2_is_dir ( ip ) ) ;
end_of_file = ( ip - > i_disksize + sdp - > sd_sb . sb_bsize - 1 ) > > shift ;
lblock = offset > > shift ;
lblock_stop = ( offset + len + sdp - > sd_sb . sb_bsize - 1 ) > > shift ;
if ( lblock_stop > end_of_file )
return 0 ;
2006-01-16 19:50:04 +03:00
2008-01-28 11:47:38 +03:00
size = ( lblock_stop - lblock ) < < shift ;
do {
bh . b_state = 0 ;
bh . b_size = size ;
gfs2_block_map ( & ip - > i_inode , lblock , & bh , 0 ) ;
if ( ! buffer_mapped ( & bh ) )
2006-01-16 19:50:04 +03:00
return 0 ;
2008-01-28 11:47:38 +03:00
size - = bh . b_size ;
lblock + = ( bh . b_size > > ip - > i_inode . i_blkbits ) ;
} while ( size > 0 ) ;
2006-01-16 19:50:04 +03:00
2008-01-28 11:47:38 +03:00
* alloc_required = 0 ;
2006-01-16 19:50:04 +03:00
return 0 ;
}