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-09-19 09:56:29 +04:00
# include <linux/lm_interface.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"
2006-07-26 19:27:10 +04:00
# include "ops_address.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 {
__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 gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
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 ) ,
ip - > i_di . di_size ) ;
memset ( kaddr + ip - > i_di . di_size , 0 ,
PAGE_CACHE_SIZE - ip - > i_di . di_size ) ;
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 ) ;
2006-09-04 19:41:31 +04:00
if ( sdp - > sd_args . ar_data = = GFS2_DATA_ORDERED | | gfs2_is_jdata ( ip ) )
2006-07-26 18:51:20 +04:00
gfs2_trans_add_bh ( ip - > i_gl , bh , 0 ) ;
mark_buffer_dirty ( bh ) ;
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
2006-01-16 19:50:04 +03:00
if ( ip - > i_di . di_size ) {
/* Get a free block, fill it with the stuffed data,
and write it out to disk */
2006-02-08 14:50:51 +03:00
if ( isdir ) {
2006-01-16 19:50:04 +03:00
block = gfs2_alloc_meta ( ip ) ;
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 {
block = gfs2_alloc_data ( ip ) ;
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 ) ) ;
if ( ip - > i_di . di_size ) {
2006-10-02 20:39:19 +04:00
* ( __be64 * ) ( di + 1 ) = cpu_to_be64 ( block ) ;
2006-01-16 19:50:04 +03:00
ip - > i_di . di_blocks + + ;
2006-11-08 23:45:46 +03:00
gfs2_set_inode_blocks ( & ip - > i_inode ) ;
2006-10-02 20:39:19 +04:00
di - > di_blocks = cpu_to_be64 ( ip - > i_di . di_blocks ) ;
2006-01-16 19:50:04 +03:00
}
ip - > i_di . di_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 ;
}
/**
* calc_tree_height - Calculate the height of a metadata tree
* @ ip : The GFS2 inode
* @ size : The proposed size of the file
*
* Work out how tall a metadata tree needs to be in order to accommodate a
* file of a particular size . If size is less than the current size of
* the inode , then the current size of the inode is used instead of the
* supplied one .
*
* Returns : the height the tree should be
*/
2006-09-04 20:49:07 +04:00
static unsigned int calc_tree_height ( 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-09-04 20:49:07 +04:00
u64 * arr ;
2006-01-16 19:50:04 +03:00
unsigned int max , height ;
if ( ip - > i_di . di_size > size )
size = ip - > i_di . di_size ;
2006-02-08 14:50:51 +03:00
if ( gfs2_is_dir ( ip ) ) {
2006-01-16 19:50:04 +03:00
arr = sdp - > sd_jheightsize ;
max = sdp - > sd_max_jheight ;
} else {
arr = sdp - > sd_heightsize ;
max = sdp - > sd_max_height ;
}
for ( height = 0 ; height < max ; height + + )
if ( arr [ height ] > = size )
break ;
return height ;
}
/**
* build_height - Build a metadata tree of the requested height
* @ ip : The GFS2 inode
* @ height : The height to build to
*
*
* Returns : errno
*/
2006-05-12 20:09:15 +04:00
static int build_height ( struct inode * inode , unsigned height )
2006-01-16 19:50:04 +03:00
{
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-05-12 20:09:15 +04:00
unsigned new_height = height - ip - > i_di . di_height ;
struct buffer_head * dibh ;
struct buffer_head * blocks [ GFS2_MAX_META_HEIGHT ] ;
2006-10-02 20:39:19 +04:00
struct gfs2_dinode * di ;
2006-01-16 19:50:04 +03:00
int error ;
2006-10-14 18:46:30 +04:00
__be64 * bp ;
2006-05-12 20:09:15 +04:00
u64 bn ;
unsigned n ;
2006-01-16 19:50:04 +03:00
2006-05-12 20:09:15 +04:00
if ( height < = ip - > i_di . di_height )
return 0 ;
2006-01-16 19:50:04 +03:00
2006-05-12 20:09:15 +04:00
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
return error ;
2006-01-16 19:50:04 +03:00
2006-05-12 20:09:15 +04:00
for ( n = 0 ; n < new_height ; n + + ) {
bn = gfs2_alloc_meta ( ip ) ;
blocks [ n ] = gfs2_meta_new ( ip - > i_gl , bn ) ;
gfs2_trans_add_bh ( ip - > i_gl , blocks [ n ] , 1 ) ;
}
2006-09-25 17:26:04 +04:00
2006-05-12 20:09:15 +04:00
n = 0 ;
bn = blocks [ 0 ] - > b_blocknr ;
if ( new_height > 1 ) {
for ( ; n < new_height - 1 ; n + + ) {
gfs2_metatype_set ( blocks [ n ] , GFS2_METATYPE_IN ,
2006-01-16 19:50:04 +03:00
GFS2_FORMAT_IN ) ;
2006-05-12 20:09:15 +04:00
gfs2_buffer_clear_tail ( blocks [ n ] ,
sizeof ( struct gfs2_meta_header ) ) ;
2006-10-14 18:46:30 +04:00
bp = ( __be64 * ) ( blocks [ n ] - > b_data +
2006-05-12 20:09:15 +04:00
sizeof ( struct gfs2_meta_header ) ) ;
* bp = cpu_to_be64 ( blocks [ n + 1 ] - > b_blocknr ) ;
brelse ( blocks [ n ] ) ;
blocks [ n ] = NULL ;
2006-01-16 19:50:04 +03:00
}
}
2006-05-12 20:09:15 +04:00
gfs2_metatype_set ( blocks [ n ] , GFS2_METATYPE_IN , GFS2_FORMAT_IN ) ;
gfs2_buffer_copy_tail ( blocks [ n ] , sizeof ( struct gfs2_meta_header ) ,
dibh , sizeof ( struct gfs2_dinode ) ) ;
brelse ( blocks [ n ] ) ;
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-05-12 20:09:15 +04:00
gfs2_buffer_clear_tail ( dibh , sizeof ( struct gfs2_dinode ) ) ;
2006-10-02 20:39:19 +04:00
* ( __be64 * ) ( di + 1 ) = cpu_to_be64 ( bn ) ;
2006-05-12 20:09:15 +04:00
ip - > i_di . di_height + = new_height ;
ip - > i_di . di_blocks + = new_height ;
2006-11-08 23:45:46 +03:00
gfs2_set_inode_blocks ( & ip - > i_inode ) ;
2006-10-02 20:39:19 +04:00
di - > di_height = cpu_to_be16 ( ip - > i_di . di_height ) ;
di - > di_blocks = cpu_to_be64 ( ip - > i_di . di_blocks ) ;
2006-05-12 20:09:15 +04:00
brelse ( dibh ) ;
return error ;
2006-01-16 19:50:04 +03:00
}
/**
* find_metapath - Find path through the metadata tree
* @ ip : The inode pointer
* @ mp : The metapath to return the result in
* @ block : The disk block to look up
*
* 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 |
* | |
* | |
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
*/
2006-09-04 20:49:07 +04:00
static void find_metapath ( struct gfs2_inode * ip , u64 block ,
2006-02-27 20:00:42 +03:00
struct metapath * mp )
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-09-04 20:49:07 +04:00
u64 b = block ;
2006-01-16 19:50:04 +03:00
unsigned int i ;
for ( i = ip - > i_di . di_height ; i - - ; )
2006-09-04 21:55:48 +04:00
mp - > mp_list [ i ] = do_div ( b , sdp - > sd_inptrs ) ;
2006-01-16 19:50:04 +03:00
}
/**
* metapointer - Return pointer to start of metadata in a buffer
* @ bh : The 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 .
*/
2006-10-14 18:46:30 +04:00
static inline __be64 * metapointer ( struct buffer_head * bh , int * boundary ,
2006-05-06 00:59:11 +04:00
unsigned int height , const struct metapath * mp )
2006-01-16 19:50:04 +03:00
{
unsigned int head_size = ( height > 0 ) ?
sizeof ( struct gfs2_meta_header ) : sizeof ( struct gfs2_dinode ) ;
2006-10-14 18:46:30 +04:00
__be64 * ptr ;
2006-05-06 00:59:11 +04:00
* boundary = 0 ;
2006-10-14 18:46:30 +04:00
ptr = ( ( __be64 * ) ( bh - > b_data + head_size ) ) + mp - > mp_list [ height ] ;
if ( ptr + 1 = = ( __be64 * ) ( bh - > b_data + bh - > b_size ) )
2006-05-06 00:59:11 +04:00
* boundary = 1 ;
return ptr ;
2006-01-16 19:50:04 +03:00
}
/**
* lookup_block - Get the next metadata block in metadata tree
* @ ip : The GFS2 inode
* @ bh : Buffer containing the pointers to metadata blocks
* @ height : The height of the tree ( 0 = dinode )
* @ mp : The metapath
* @ create : Non - zero if we may create a new meatdata block
* @ new : Used to indicate if we did create a new metadata block
* @ block : the returned disk block number
*
* Given a metatree , complete to a particular height , checks to see if the next
* height of the tree exists . If not the next height of the tree is created .
* The block number of the next height of the metadata tree is returned .
*
*/
2006-05-06 00:59:11 +04:00
static int lookup_block ( struct gfs2_inode * ip , struct buffer_head * bh ,
unsigned int height , struct metapath * mp , int create ,
2006-09-04 20:49:07 +04:00
int * new , u64 * block )
2006-01-16 19:50:04 +03:00
{
2006-05-06 00:59:11 +04:00
int boundary ;
2006-10-14 18:46:30 +04:00
__be64 * ptr = metapointer ( bh , & boundary , height , mp ) ;
2006-01-16 19:50:04 +03:00
if ( * ptr ) {
* block = be64_to_cpu ( * ptr ) ;
2006-05-06 00:59:11 +04:00
return boundary ;
2006-01-16 19:50:04 +03:00
}
* block = 0 ;
if ( ! create )
2006-05-06 00:59:11 +04:00
return 0 ;
2006-01-16 19:50:04 +03:00
2006-05-06 00:59:11 +04:00
if ( height = = ip - > i_di . di_height - 1 & & ! gfs2_is_dir ( ip ) )
2006-01-16 19:50:04 +03:00
* block = gfs2_alloc_data ( ip ) ;
else
* block = gfs2_alloc_meta ( ip ) ;
2006-01-18 14:19:28 +03:00
gfs2_trans_add_bh ( ip - > i_gl , bh , 1 ) ;
2006-01-16 19:50:04 +03:00
* ptr = cpu_to_be64 ( * block ) ;
ip - > i_di . di_blocks + + ;
2006-11-08 23:45:46 +03:00
gfs2_set_inode_blocks ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
* new = 1 ;
2006-05-06 00:59:11 +04:00
return 0 ;
2006-01-16 19:50:04 +03:00
}
2006-11-15 23:21:06 +03:00
static inline void bmap_lock ( struct inode * inode , int create )
{
struct gfs2_inode * ip = GFS2_I ( inode ) ;
if ( create )
down_write ( & ip - > i_rw_mutex ) ;
else
down_read ( & ip - > i_rw_mutex ) ;
}
static inline void bmap_unlock ( struct inode * inode , int create )
{
struct gfs2_inode * ip = GFS2_I ( inode ) ;
if ( create )
up_write ( & ip - > i_rw_mutex ) ;
else
up_read ( & ip - > i_rw_mutex ) ;
}
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
2006-01-16 19:50:04 +03:00
*
* Find the block number on the current device which corresponds to an
* inode ' s block . If the block had to be created , " new " will be set .
*
* Returns : errno
*/
2006-11-15 23:21:06 +03:00
int gfs2_block_map ( struct inode * inode , u64 lblock , int create ,
struct buffer_head * bh_map )
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 ) ;
2006-01-16 19:50:04 +03:00
struct buffer_head * bh ;
unsigned int bsize ;
unsigned int height ;
unsigned int end_of_metadata ;
unsigned int x ;
int error = 0 ;
2006-09-19 01:18:23 +04:00
int new = 0 ;
u64 dblock = 0 ;
int boundary ;
2006-10-14 01:25:45 +04:00
unsigned int maxlen = bh_map - > b_size > > inode - > i_blkbits ;
2006-11-15 23:21:06 +03:00
struct metapath mp ;
u64 size ;
2006-01-16 19:50:04 +03:00
2006-09-22 01:05:23 +04:00
BUG_ON ( maxlen = = 0 ) ;
2006-01-16 19:50:04 +03:00
if ( gfs2_assert_warn ( sdp , ! gfs2_is_stuffed ( ip ) ) )
2006-09-19 01:18:23 +04:00
return 0 ;
2006-01-16 19:50:04 +03:00
2006-11-15 23:21:06 +03:00
bmap_lock ( inode , create ) ;
clear_buffer_mapped ( bh_map ) ;
clear_buffer_new ( bh_map ) ;
clear_buffer_boundary ( bh_map ) ;
2006-09-04 19:41:31 +04:00
bsize = gfs2_is_dir ( ip ) ? sdp - > sd_jbsize : sdp - > sd_sb . sb_bsize ;
2006-11-15 23:21:06 +03:00
size = ( lblock + 1 ) * bsize ;
if ( size > ip - > i_di . di_size ) {
height = calc_tree_height ( ip , size ) ;
if ( ip - > i_di . di_height < height ) {
if ( ! create )
goto out_ok ;
error = build_height ( inode , height ) ;
if ( error )
goto out_fail ;
}
2006-01-16 19:50:04 +03:00
}
2006-11-15 23:21:06 +03:00
find_metapath ( ip , lblock , & mp ) ;
2006-01-16 19:50:04 +03:00
end_of_metadata = ip - > i_di . di_height - 1 ;
error = gfs2_meta_inode_buffer ( ip , & bh ) ;
if ( error )
2006-11-15 23:21:06 +03:00
goto out_fail ;
2006-01-16 19:50:04 +03:00
for ( x = 0 ; x < end_of_metadata ; x + + ) {
2006-11-15 23:21:06 +03:00
lookup_block ( ip , bh , x , & mp , create , & new , & dblock ) ;
2006-01-16 19:50:04 +03:00
brelse ( bh ) ;
2006-09-19 01:18:23 +04:00
if ( ! dblock )
2006-11-15 23:21:06 +03:00
goto out_ok ;
2006-01-16 19:50:04 +03:00
2006-09-19 01:18:23 +04:00
error = gfs2_meta_indirect_buffer ( ip , x + 1 , dblock , new , & bh ) ;
2006-01-16 19:50:04 +03:00
if ( error )
2006-11-15 23:21:06 +03:00
goto out_fail ;
2006-01-16 19:50:04 +03:00
}
2006-11-15 23:21:06 +03:00
boundary = lookup_block ( ip , bh , end_of_metadata , & mp , create , & new , & dblock ) ;
2006-09-19 01:18:23 +04:00
if ( dblock ) {
map_bh ( bh_map , inode - > i_sb , dblock ) ;
if ( boundary )
2006-11-15 23:21:06 +03:00
set_buffer_boundary ( bh_map ) ;
2006-09-19 01:18:23 +04:00
if ( new ) {
struct buffer_head * dibh ;
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( ! error ) {
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-09-19 01:18:23 +04:00
brelse ( dibh ) ;
}
set_buffer_new ( bh_map ) ;
goto out_brelse ;
}
while ( - - maxlen & & ! buffer_boundary ( bh_map ) ) {
u64 eblock ;
2006-11-15 23:21:06 +03:00
mp . mp_list [ end_of_metadata ] + + ;
boundary = lookup_block ( ip , bh , end_of_metadata , & mp , 0 , & new , & eblock ) ;
2006-09-19 01:18:23 +04:00
if ( eblock ! = + + dblock )
break ;
2006-09-19 09:16:03 +04:00
bh_map - > b_size + = ( 1 < < inode - > i_blkbits ) ;
2006-09-19 01:18:23 +04:00
if ( boundary )
set_buffer_boundary ( bh_map ) ;
2006-01-16 19:50:04 +03:00
}
}
2006-09-19 01:18:23 +04:00
out_brelse :
brelse ( bh ) ;
2006-11-15 23:21:06 +03:00
out_ok :
error = 0 ;
out_fail :
2006-05-06 00:59:11 +04:00
bmap_unlock ( inode , create ) ;
2006-11-15 23:21:06 +03:00
return error ;
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 ) ;
2006-10-14 01:25:45 +04:00
bh . b_size = 1 < < ( inode - > i_blkbits + 5 ) ;
2006-11-15 23:21:06 +03:00
ret = gfs2_block_map ( inode , lblock , create , & bh ) ;
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 ;
if ( height < ip - > i_di . di_height - 1 )
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 ;
}
2006-02-08 14:50:51 +03:00
metadata = ( height ! = ip - > i_di . di_height - 1 ) ;
2006-01-16 19:50:04 +03:00
if ( metadata )
revokes = ( height ) ? sdp - > sd_inptrs : sdp - > sd_diptrs ;
error = gfs2_rindex_hold ( sdp , & ip - > i_alloc . al_ri_gh ) ;
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 */
gfs2_rlist_alloc ( & rlist , LM_ST_EXCLUSIVE , 0 ) ;
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 ;
if ( ! ip - > i_di . di_blocks )
gfs2_consist_inode ( ip ) ;
ip - > i_di . di_blocks - - ;
2006-11-08 23:45:46 +03:00
gfs2_set_inode_blocks ( & ip - > i_inode ) ;
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 :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & ip - > i_alloc . al_ri_gh ) ;
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 ;
unsigned int h ;
int error ;
al = gfs2_alloc_get ( ip ) ;
error = gfs2_quota_lock ( ip , NO_QUOTA_CHANGE , NO_QUOTA_CHANGE ) ;
if ( error )
goto out ;
2006-11-01 21:23:29 +03:00
error = gfs2_quota_check ( ip , ip - > i_inode . i_uid , ip - > i_inode . i_gid ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_gunlock_q ;
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 ;
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 )
goto out_end_trans ;
}
h = calc_tree_height ( ip , size ) ;
if ( ip - > i_di . di_height < h ) {
down_write ( & ip - > i_rw_mutex ) ;
2006-06-14 23:32:57 +04:00
error = build_height ( & ip - > i_inode , h ) ;
2006-01-16 19:50:04 +03:00
up_write ( & ip - > i_rw_mutex ) ;
if ( error )
goto out_end_trans ;
}
}
ip - > i_di . di_size = size ;
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
error = gfs2_meta_inode_buffer ( ip , & dibh ) ;
if ( error )
goto out_end_trans ;
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_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 ) ;
struct gfs2_sbd * sdp = GFS2_SB ( 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 ) ) {
gfs2_get_block ( inode , iblock , bh , 0 ) ;
/* 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
}
if ( sdp - > sd_args . ar_data = = GFS2_DATA_ORDERED | | gfs2_is_jdata ( ip ) )
gfs2_trans_add_bh ( ip - > i_gl , bh , 0 ) ;
2007-05-11 09:41:28 +04:00
zero_user_page ( page , offset , length , KM_USER0 ) ;
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 ) ) {
ip - > i_di . di_size = 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 ) {
ip - > i_di . di_size = size ;
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
ip - > i_di . di_flags | = 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
{
unsigned int height = ip - > i_di . di_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
2006-06-14 23:32:57 +04:00
lblock = ( size - 1 ) > > GFS2_SB ( & ip - > i_inode ) - > sd_sb . sb_bsize_shift ;
2006-01-16 19:50:04 +03:00
find_metapath ( ip , lblock , & mp ) ;
gfs2_alloc_get ( ip ) ;
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 ;
if ( ! ip - > i_di . di_size ) {
ip - > i_di . di_height = 0 ;
ip - > i_di . di_goal_meta =
ip - > i_di . di_goal_data =
2007-05-15 18:37:50 +04:00
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 ;
2006-01-16 19:50:04 +03:00
ip - > i_di . di_flags & = ~ 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
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 ;
}
/**
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 ;
if ( size > ip - > i_di . di_size )
error = do_grow ( ip , size ) ;
else
2006-01-24 13:37:06 +03:00
error = do_shrink ( ip , size ) ;
2006-01-16 19:50:04 +03:00
return error ;
}
int gfs2_truncatei_resume ( struct gfs2_inode * ip )
{
int error ;
error = trunc_dealloc ( ip , ip - > i_di . di_size ) ;
if ( ! error )
error = trunc_end ( ip ) ;
return error ;
}
int gfs2_file_dealloc ( struct gfs2_inode * ip )
{
return trunc_dealloc ( ip , 0 ) ;
}
/**
* gfs2_write_calc_reserv - calculate number of blocks needed to write to a file
* @ ip : the file
* @ len : the number of bytes to be written to the file
* @ data_blocks : returns the number of data blocks required
* @ ind_blocks : returns the number of indirect blocks required
*
*/
void gfs2_write_calc_reserv ( struct gfs2_inode * ip , unsigned int len ,
unsigned int * data_blocks , unsigned int * ind_blocks )
{
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
unsigned int tmp ;
2006-02-08 14:50:51 +03:00
if ( gfs2_is_dir ( ip ) ) {
2006-02-28 01:23:27 +03:00
* data_blocks = DIV_ROUND_UP ( len , sdp - > sd_jbsize ) + 2 ;
2006-01-16 19:50:04 +03:00
* ind_blocks = 3 * ( sdp - > sd_max_jheight - 1 ) ;
} else {
* data_blocks = ( len > > sdp - > sd_sb . sb_bsize_shift ) + 3 ;
* ind_blocks = 3 * ( sdp - > sd_max_height - 1 ) ;
}
for ( tmp = * data_blocks ; tmp > sdp - > sd_diptrs ; ) {
2006-02-28 01:23:27 +03:00
tmp = DIV_ROUND_UP ( tmp , sdp - > sd_inptrs ) ;
2006-01-16 19:50:04 +03:00
* ind_blocks + = tmp ;
}
}
/**
* 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 ) ;
2006-09-04 20:49:07 +04:00
u64 lblock , lblock_stop , dblock ;
u32 extlen ;
2006-01-16 19:50:04 +03:00
int new = 0 ;
int error = 0 ;
* 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 ;
}
2006-02-08 14:50:51 +03:00
if ( gfs2_is_dir ( ip ) ) {
2006-01-16 19:50:04 +03:00
unsigned int bsize = sdp - > sd_jbsize ;
lblock = offset ;
do_div ( lblock , bsize ) ;
lblock_stop = offset + len + bsize - 1 ;
do_div ( lblock_stop , bsize ) ;
} else {
unsigned int shift = sdp - > sd_sb . sb_bsize_shift ;
lblock = offset > > shift ;
lblock_stop = ( offset + len + sdp - > sd_sb . sb_bsize - 1 ) > > shift ;
}
for ( ; lblock < lblock_stop ; lblock + = extlen ) {
2006-06-14 23:32:57 +04:00
error = gfs2_extent_map ( & ip - > i_inode , lblock , & new , & dblock , & extlen ) ;
2006-01-16 19:50:04 +03:00
if ( error )
return error ;
if ( ! dblock ) {
* alloc_required = 1 ;
return 0 ;
}
}
return 0 ;
}