2008-07-14 20:08:37 +04:00
/*
* This file is part of UBIFS .
*
* Copyright ( C ) 2006 - 2008 Nokia Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . , 51
* Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
* Authors : Artem Bityutskiy ( Б и т ю ц к и й А р т ё м )
* Adrian Hunter
*/
/*
* This file is a part of UBIFS journal implementation and contains various
* functions which manipulate the log . The log is a fixed area on the flash
* which does not contain any data but refers to buds . The log is a part of the
* journal .
*/
# include "ubifs.h"
# ifdef CONFIG_UBIFS_FS_DEBUG
static int dbg_check_bud_bytes ( struct ubifs_info * c ) ;
# else
# define dbg_check_bud_bytes(c) 0
# endif
/**
* ubifs_search_bud - search bud LEB .
* @ c : UBIFS file - system description object
* @ lnum : logical eraseblock number to search
*
* This function searches bud LEB @ lnum . Returns bud description object in case
* of success and % NULL if there is no bud with this LEB number .
*/
struct ubifs_bud * ubifs_search_bud ( struct ubifs_info * c , int lnum )
{
struct rb_node * p ;
struct ubifs_bud * bud ;
spin_lock ( & c - > buds_lock ) ;
p = c - > buds . rb_node ;
while ( p ) {
bud = rb_entry ( p , struct ubifs_bud , rb ) ;
if ( lnum < bud - > lnum )
p = p - > rb_left ;
else if ( lnum > bud - > lnum )
p = p - > rb_right ;
else {
spin_unlock ( & c - > buds_lock ) ;
return bud ;
}
}
spin_unlock ( & c - > buds_lock ) ;
return NULL ;
}
/**
* ubifs_get_wbuf - get the wbuf associated with a LEB , if there is one .
* @ c : UBIFS file - system description object
* @ lnum : logical eraseblock number to search
*
* This functions returns the wbuf for @ lnum or % NULL if there is not one .
*/
struct ubifs_wbuf * ubifs_get_wbuf ( struct ubifs_info * c , int lnum )
{
struct rb_node * p ;
struct ubifs_bud * bud ;
int jhead ;
if ( ! c - > jheads )
return NULL ;
spin_lock ( & c - > buds_lock ) ;
p = c - > buds . rb_node ;
while ( p ) {
bud = rb_entry ( p , struct ubifs_bud , rb ) ;
if ( lnum < bud - > lnum )
p = p - > rb_left ;
else if ( lnum > bud - > lnum )
p = p - > rb_right ;
else {
jhead = bud - > jhead ;
spin_unlock ( & c - > buds_lock ) ;
return & c - > jheads [ jhead ] . wbuf ;
}
}
spin_unlock ( & c - > buds_lock ) ;
return NULL ;
}
/**
* next_log_lnum - switch to the next log LEB .
* @ c : UBIFS file - system description object
* @ lnum : current log LEB
*/
static inline int next_log_lnum ( const struct ubifs_info * c , int lnum )
{
lnum + = 1 ;
if ( lnum > c - > log_last )
lnum = UBIFS_LOG_LNUM ;
return lnum ;
}
/**
* empty_log_bytes - calculate amount of empty space in the log .
* @ c : UBIFS file - system description object
*/
static inline long long empty_log_bytes ( const struct ubifs_info * c )
{
long long h , t ;
h = ( long long ) c - > lhead_lnum * c - > leb_size + c - > lhead_offs ;
t = ( long long ) c - > ltail_lnum * c - > leb_size ;
if ( h > = t )
return c - > log_bytes - h + t ;
else
return t - h ;
}
/**
* ubifs_add_bud - add bud LEB to the tree of buds and its journal head list .
* @ c : UBIFS file - system description object
* @ bud : the bud to add
*/
void ubifs_add_bud ( struct ubifs_info * c , struct ubifs_bud * bud )
{
struct rb_node * * p , * parent = NULL ;
struct ubifs_bud * b ;
struct ubifs_jhead * jhead ;
spin_lock ( & c - > buds_lock ) ;
p = & c - > buds . rb_node ;
while ( * p ) {
parent = * p ;
b = rb_entry ( parent , struct ubifs_bud , rb ) ;
ubifs_assert ( bud - > lnum ! = b - > lnum ) ;
if ( bud - > lnum < b - > lnum )
p = & ( * p ) - > rb_left ;
else
p = & ( * p ) - > rb_right ;
}
rb_link_node ( & bud - > rb , parent , p ) ;
rb_insert_color ( & bud - > rb , & c - > buds ) ;
if ( c - > jheads ) {
jhead = & c - > jheads [ bud - > jhead ] ;
list_add_tail ( & bud - > list , & jhead - > buds_list ) ;
} else
ubifs_assert ( c - > replaying & & ( c - > vfs_sb - > s_flags & MS_RDONLY ) ) ;
/*
* Note , although this is a new bud , we anyway account this space now ,
* before any data has been written to it , because this is about to
* guarantee fixed mount time , and this bud will anyway be read and
* scanned .
*/
c - > bud_bytes + = c - > leb_size - bud - > start ;
2009-09-15 16:03:51 +04:00
dbg_log ( " LEB %d:%d, jhead %s, bud_bytes %lld " , bud - > lnum ,
bud - > start , dbg_jhead ( bud - > jhead ) , c - > bud_bytes ) ;
2008-07-14 20:08:37 +04:00
spin_unlock ( & c - > buds_lock ) ;
}
/**
* ubifs_create_buds_lists - create journal head buds lists for remount rw .
* @ c : UBIFS file - system description object
*/
void ubifs_create_buds_lists ( struct ubifs_info * c )
{
struct rb_node * p ;
spin_lock ( & c - > buds_lock ) ;
p = rb_first ( & c - > buds ) ;
while ( p ) {
struct ubifs_bud * bud = rb_entry ( p , struct ubifs_bud , rb ) ;
struct ubifs_jhead * jhead = & c - > jheads [ bud - > jhead ] ;
list_add_tail ( & bud - > list , & jhead - > buds_list ) ;
p = rb_next ( p ) ;
}
spin_unlock ( & c - > buds_lock ) ;
}
/**
* ubifs_add_bud_to_log - add a new bud to the log .
* @ c : UBIFS file - system description object
* @ jhead : journal head the bud belongs to
* @ lnum : LEB number of the bud
* @ offs : starting offset of the bud
*
* This function writes reference node for the new bud LEB @ lnum it to the log ,
* and adds it to the buds tress . It also makes sure that log size does not
* exceed the ' c - > max_bud_bytes ' limit . Returns zero in case of success ,
* % - EAGAIN if commit is required , and a negative error codes in case of
* failure .
*/
int ubifs_add_bud_to_log ( struct ubifs_info * c , int jhead , int lnum , int offs )
{
int err ;
struct ubifs_bud * bud ;
struct ubifs_ref_node * ref ;
bud = kmalloc ( sizeof ( struct ubifs_bud ) , GFP_NOFS ) ;
if ( ! bud )
return - ENOMEM ;
ref = kzalloc ( c - > ref_node_alsz , GFP_NOFS ) ;
if ( ! ref ) {
kfree ( bud ) ;
return - ENOMEM ;
}
mutex_lock ( & c - > log_mutex ) ;
if ( c - > ro_media ) {
err = - EROFS ;
goto out_unlock ;
}
/* Make sure we have enough space in the log */
if ( empty_log_bytes ( c ) - c - > ref_node_alsz < c - > min_log_bytes ) {
dbg_log ( " not enough log space - %lld, required %d " ,
empty_log_bytes ( c ) , c - > min_log_bytes ) ;
ubifs_commit_required ( c ) ;
err = - EAGAIN ;
goto out_unlock ;
}
/*
2009-03-20 20:11:12 +03:00
* Make sure the amount of space in buds will not exceed the
2008-07-14 20:08:37 +04:00
* ' c - > max_bud_bytes ' limit , because we want to guarantee mount time
* limits .
*
* It is not necessary to hold @ c - > buds_lock when reading @ c - > bud_bytes
* because we are holding @ c - > log_mutex . All @ c - > bud_bytes take place
* when both @ c - > log_mutex and @ c - > bud_bytes are locked .
*/
if ( c - > bud_bytes + c - > leb_size - offs > c - > max_bud_bytes ) {
dbg_log ( " bud bytes %lld (%lld max), require commit " ,
c - > bud_bytes , c - > max_bud_bytes ) ;
ubifs_commit_required ( c ) ;
err = - EAGAIN ;
goto out_unlock ;
}
/*
* If the journal is full enough - start background commit . Note , it is
* OK to read ' c - > cmt_state ' without spinlock because integer reads
* are atomic in the kernel .
*/
if ( c - > bud_bytes > = c - > bg_bud_bytes & &
c - > cmt_state = = COMMIT_RESTING ) {
dbg_log ( " bud bytes %lld (%lld max), initiate BG commit " ,
c - > bud_bytes , c - > max_bud_bytes ) ;
ubifs_request_bg_commit ( c ) ;
}
bud - > lnum = lnum ;
bud - > start = offs ;
bud - > jhead = jhead ;
ref - > ch . node_type = UBIFS_REF_NODE ;
ref - > lnum = cpu_to_le32 ( bud - > lnum ) ;
ref - > offs = cpu_to_le32 ( bud - > start ) ;
ref - > jhead = cpu_to_le32 ( jhead ) ;
if ( c - > lhead_offs > c - > leb_size - c - > ref_node_alsz ) {
c - > lhead_lnum = next_log_lnum ( c , c - > lhead_lnum ) ;
c - > lhead_offs = 0 ;
}
if ( c - > lhead_offs = = 0 ) {
/* Must ensure next log LEB has been unmapped */
err = ubifs_leb_unmap ( c , c - > lhead_lnum ) ;
if ( err )
goto out_unlock ;
}
if ( bud - > start = = 0 ) {
/*
* Before writing the LEB reference which refers an empty LEB
* to the log , we have to make sure it is mapped , because
* otherwise we ' d risk to refer an LEB with garbage in case of
* an unclean reboot , because the target LEB might have been
* unmapped , but not yet physically erased .
*/
err = ubi_leb_map ( c - > ubi , bud - > lnum , UBI_SHORTTERM ) ;
if ( err )
goto out_unlock ;
}
dbg_log ( " write ref LEB %d:%d " ,
c - > lhead_lnum , c - > lhead_offs ) ;
err = ubifs_write_node ( c , ref , UBIFS_REF_NODE_SZ , c - > lhead_lnum ,
c - > lhead_offs , UBI_SHORTTERM ) ;
if ( err )
goto out_unlock ;
c - > lhead_offs + = c - > ref_node_alsz ;
ubifs_add_bud ( c , bud ) ;
mutex_unlock ( & c - > log_mutex ) ;
kfree ( ref ) ;
return 0 ;
out_unlock :
2008-07-18 18:56:37 +04:00
if ( err ! = - EAGAIN )
ubifs_ro_mode ( c , err ) ;
2008-07-14 20:08:37 +04:00
mutex_unlock ( & c - > log_mutex ) ;
kfree ( ref ) ;
kfree ( bud ) ;
return err ;
}
/**
* remove_buds - remove used buds .
* @ c : UBIFS file - system description object
*
* This function removes use buds from the buds tree . It does not remove the
* buds which are pointed to by journal heads .
*/
static void remove_buds ( struct ubifs_info * c )
{
struct rb_node * p ;
ubifs_assert ( list_empty ( & c - > old_buds ) ) ;
c - > cmt_bud_bytes = 0 ;
spin_lock ( & c - > buds_lock ) ;
p = rb_first ( & c - > buds ) ;
while ( p ) {
struct rb_node * p1 = p ;
struct ubifs_bud * bud ;
struct ubifs_wbuf * wbuf ;
p = rb_next ( p ) ;
bud = rb_entry ( p1 , struct ubifs_bud , rb ) ;
wbuf = & c - > jheads [ bud - > jhead ] . wbuf ;
if ( wbuf - > lnum = = bud - > lnum ) {
/*
* Do not remove buds which are pointed to by journal
* heads ( non - closed buds ) .
*/
c - > cmt_bud_bytes + = wbuf - > offs - bud - > start ;
2009-09-15 16:03:51 +04:00
dbg_log ( " preserve %d:%d, jhead %s, bud bytes %d, "
2008-07-14 20:08:37 +04:00
" cmt_bud_bytes %lld " , bud - > lnum , bud - > start ,
2009-09-15 16:03:51 +04:00
dbg_jhead ( bud - > jhead ) , wbuf - > offs - bud - > start ,
2008-07-14 20:08:37 +04:00
c - > cmt_bud_bytes ) ;
bud - > start = wbuf - > offs ;
} else {
c - > cmt_bud_bytes + = c - > leb_size - bud - > start ;
2009-09-15 16:03:51 +04:00
dbg_log ( " remove %d:%d, jhead %s, bud bytes %d, "
2008-07-14 20:08:37 +04:00
" cmt_bud_bytes %lld " , bud - > lnum , bud - > start ,
2009-09-15 16:03:51 +04:00
dbg_jhead ( bud - > jhead ) , c - > leb_size - bud - > start ,
2008-07-14 20:08:37 +04:00
c - > cmt_bud_bytes ) ;
rb_erase ( p1 , & c - > buds ) ;
/*
* If the commit does not finish , the recovery will need
* to replay the journal , in which case the old buds
* must be unchanged . Do not release them until post
* commit i . e . do not allow them to be garbage
* collected .
*/
2009-02-13 11:13:11 +03:00
list_move ( & bud - > list , & c - > old_buds ) ;
2008-07-14 20:08:37 +04:00
}
}
spin_unlock ( & c - > buds_lock ) ;
}
/**
* ubifs_log_start_commit - start commit .
* @ c : UBIFS file - system description object
* @ ltail_lnum : return new log tail LEB number
*
* The commit operation starts with writing " commit start " node to the log and
* reference nodes for all journal heads which will define new journal after
* the commit has been finished . The commit start and reference nodes are
* written in one go to the nearest empty log LEB ( hence , when commit is
* finished UBIFS may safely unmap all the previous log LEBs ) . This function
* returns zero in case of success and a negative error code in case of
* failure .
*/
int ubifs_log_start_commit ( struct ubifs_info * c , int * ltail_lnum )
{
void * buf ;
struct ubifs_cs_node * cs ;
struct ubifs_ref_node * ref ;
int err , i , max_len , len ;
err = dbg_check_bud_bytes ( c ) ;
if ( err )
return err ;
max_len = UBIFS_CS_NODE_SZ + c - > jhead_cnt * UBIFS_REF_NODE_SZ ;
max_len = ALIGN ( max_len , c - > min_io_size ) ;
buf = cs = kmalloc ( max_len , GFP_NOFS ) ;
if ( ! buf )
return - ENOMEM ;
cs - > ch . node_type = UBIFS_CS_NODE ;
2008-07-21 18:14:29 +04:00
cs - > cmt_no = cpu_to_le64 ( c - > cmt_no ) ;
2008-07-14 20:08:37 +04:00
ubifs_prepare_node ( c , cs , UBIFS_CS_NODE_SZ , 0 ) ;
/*
* Note , we do not lock ' c - > log_mutex ' because this is the commit start
* phase and we are exclusively using the log . And we do not lock
* write - buffer because nobody can write to the file - system at this
* phase .
*/
len = UBIFS_CS_NODE_SZ ;
for ( i = 0 ; i < c - > jhead_cnt ; i + + ) {
int lnum = c - > jheads [ i ] . wbuf . lnum ;
int offs = c - > jheads [ i ] . wbuf . offs ;
if ( lnum = = - 1 | | offs = = c - > leb_size )
continue ;
2009-09-15 16:03:51 +04:00
dbg_log ( " add ref to LEB %d:%d for jhead %s " ,
lnum , offs , dbg_jhead ( i ) ) ;
2008-07-14 20:08:37 +04:00
ref = buf + len ;
ref - > ch . node_type = UBIFS_REF_NODE ;
ref - > lnum = cpu_to_le32 ( lnum ) ;
ref - > offs = cpu_to_le32 ( offs ) ;
ref - > jhead = cpu_to_le32 ( i ) ;
ubifs_prepare_node ( c , ref , UBIFS_REF_NODE_SZ , 0 ) ;
len + = UBIFS_REF_NODE_SZ ;
}
ubifs_pad ( c , buf + len , ALIGN ( len , c - > min_io_size ) - len ) ;
/* Switch to the next log LEB */
if ( c - > lhead_offs ) {
c - > lhead_lnum = next_log_lnum ( c , c - > lhead_lnum ) ;
c - > lhead_offs = 0 ;
}
if ( c - > lhead_offs = = 0 ) {
/* Must ensure next LEB has been unmapped */
err = ubifs_leb_unmap ( c , c - > lhead_lnum ) ;
if ( err )
goto out ;
}
len = ALIGN ( len , c - > min_io_size ) ;
dbg_log ( " writing commit start at LEB %d:0, len %d " , c - > lhead_lnum , len ) ;
err = ubifs_leb_write ( c , c - > lhead_lnum , cs , 0 , len , UBI_SHORTTERM ) ;
if ( err )
goto out ;
* ltail_lnum = c - > lhead_lnum ;
c - > lhead_offs + = len ;
if ( c - > lhead_offs = = c - > leb_size ) {
c - > lhead_lnum = next_log_lnum ( c , c - > lhead_lnum ) ;
c - > lhead_offs = 0 ;
}
remove_buds ( c ) ;
/*
* We have started the commit and now users may use the rest of the log
* for new writes .
*/
c - > min_log_bytes = 0 ;
out :
kfree ( buf ) ;
return err ;
}
/**
* ubifs_log_end_commit - end commit .
* @ c : UBIFS file - system description object
* @ ltail_lnum : new log tail LEB number
*
* This function is called on when the commit operation was finished . It
* moves log tail to new position and unmaps LEBs which contain obsolete data .
* Returns zero in case of success and a negative error code in case of
* failure .
*/
int ubifs_log_end_commit ( struct ubifs_info * c , int ltail_lnum )
{
int err ;
/*
* At this phase we have to lock ' c - > log_mutex ' because UBIFS allows FS
* writes during commit . Its only short " commit " start phase when
* writers are blocked .
*/
mutex_lock ( & c - > log_mutex ) ;
dbg_log ( " old tail was LEB %d:0, new tail is LEB %d:0 " ,
c - > ltail_lnum , ltail_lnum ) ;
c - > ltail_lnum = ltail_lnum ;
/*
* The commit is finished and from now on it must be guaranteed that
* there is always enough space for the next commit .
*/
c - > min_log_bytes = c - > leb_size ;
spin_lock ( & c - > buds_lock ) ;
c - > bud_bytes - = c - > cmt_bud_bytes ;
spin_unlock ( & c - > buds_lock ) ;
err = dbg_check_bud_bytes ( c ) ;
mutex_unlock ( & c - > log_mutex ) ;
return err ;
}
/**
* ubifs_log_post_commit - things to do after commit is completed .
* @ c : UBIFS file - system description object
* @ old_ltail_lnum : old log tail LEB number
*
* Release buds only after commit is completed , because they must be unchanged
* if recovery is needed .
*
* Unmap log LEBs only after commit is completed , because they may be needed for
* recovery .
*
* This function returns % 0 on success and a negative error code on failure .
*/
int ubifs_log_post_commit ( struct ubifs_info * c , int old_ltail_lnum )
{
int lnum , err = 0 ;
while ( ! list_empty ( & c - > old_buds ) ) {
struct ubifs_bud * bud ;
bud = list_entry ( c - > old_buds . next , struct ubifs_bud , list ) ;
err = ubifs_return_leb ( c , bud - > lnum ) ;
if ( err )
return err ;
list_del ( & bud - > list ) ;
kfree ( bud ) ;
}
mutex_lock ( & c - > log_mutex ) ;
for ( lnum = old_ltail_lnum ; lnum ! = c - > ltail_lnum ;
lnum = next_log_lnum ( c , lnum ) ) {
dbg_log ( " unmap log LEB %d " , lnum ) ;
err = ubifs_leb_unmap ( c , lnum ) ;
if ( err )
goto out ;
}
out :
mutex_unlock ( & c - > log_mutex ) ;
return err ;
}
/**
* struct done_ref - references that have been done .
* @ rb : rb - tree node
* @ lnum : LEB number
*/
struct done_ref {
struct rb_node rb ;
int lnum ;
} ;
/**
* done_already - determine if a reference has been done already .
* @ done_tree : rb - tree to store references that have been done
* @ lnum : LEB number of reference
*
* This function returns % 1 if the reference has been done , % 0 if not , otherwise
* a negative error code is returned .
*/
static int done_already ( struct rb_root * done_tree , int lnum )
{
struct rb_node * * p = & done_tree - > rb_node , * parent = NULL ;
struct done_ref * dr ;
while ( * p ) {
parent = * p ;
dr = rb_entry ( parent , struct done_ref , rb ) ;
if ( lnum < dr - > lnum )
p = & ( * p ) - > rb_left ;
else if ( lnum > dr - > lnum )
p = & ( * p ) - > rb_right ;
else
return 1 ;
}
dr = kzalloc ( sizeof ( struct done_ref ) , GFP_NOFS ) ;
if ( ! dr )
return - ENOMEM ;
dr - > lnum = lnum ;
rb_link_node ( & dr - > rb , parent , p ) ;
rb_insert_color ( & dr - > rb , done_tree ) ;
return 0 ;
}
/**
* destroy_done_tree - destroy the done tree .
* @ done_tree : done tree to destroy
*/
static void destroy_done_tree ( struct rb_root * done_tree )
{
struct rb_node * this = done_tree - > rb_node ;
struct done_ref * dr ;
while ( this ) {
if ( this - > rb_left ) {
this = this - > rb_left ;
continue ;
} else if ( this - > rb_right ) {
this = this - > rb_right ;
continue ;
}
dr = rb_entry ( this , struct done_ref , rb ) ;
this = rb_parent ( this ) ;
if ( this ) {
if ( this - > rb_left = = & dr - > rb )
this - > rb_left = NULL ;
else
this - > rb_right = NULL ;
}
kfree ( dr ) ;
}
}
/**
* add_node - add a node to the consolidated log .
* @ c : UBIFS file - system description object
* @ buf : buffer to which to add
* @ lnum : LEB number to which to write is passed and returned here
* @ offs : offset to where to write is passed and returned here
* @ node : node to add
*
* This function returns % 0 on success and a negative error code on failure .
*/
static int add_node ( struct ubifs_info * c , void * buf , int * lnum , int * offs ,
void * node )
{
struct ubifs_ch * ch = node ;
int len = le32_to_cpu ( ch - > len ) , remains = c - > leb_size - * offs ;
if ( len > remains ) {
int sz = ALIGN ( * offs , c - > min_io_size ) , err ;
ubifs_pad ( c , buf + * offs , sz - * offs ) ;
err = ubifs_leb_change ( c , * lnum , buf , sz , UBI_SHORTTERM ) ;
if ( err )
return err ;
* lnum = next_log_lnum ( c , * lnum ) ;
* offs = 0 ;
}
memcpy ( buf + * offs , node , len ) ;
* offs + = ALIGN ( len , 8 ) ;
return 0 ;
}
/**
* ubifs_consolidate_log - consolidate the log .
* @ c : UBIFS file - system description object
*
* Repeated failed commits could cause the log to be full , but at least 1 LEB is
* needed for commit . This function rewrites the reference nodes in the log
* omitting duplicates , and failed CS nodes , and leaving no gaps .
*
* This function returns % 0 on success and a negative error code on failure .
*/
int ubifs_consolidate_log ( struct ubifs_info * c )
{
struct ubifs_scan_leb * sleb ;
struct ubifs_scan_node * snod ;
struct rb_root done_tree = RB_ROOT ;
int lnum , err , first = 1 , write_lnum , offs = 0 ;
void * buf ;
dbg_rcvry ( " log tail LEB %d, log head LEB %d " , c - > ltail_lnum ,
c - > lhead_lnum ) ;
buf = vmalloc ( c - > leb_size ) ;
if ( ! buf )
return - ENOMEM ;
lnum = c - > ltail_lnum ;
write_lnum = lnum ;
while ( 1 ) {
2009-08-25 16:00:55 +04:00
sleb = ubifs_scan ( c , lnum , 0 , c - > sbuf , 0 ) ;
2008-07-14 20:08:37 +04:00
if ( IS_ERR ( sleb ) ) {
err = PTR_ERR ( sleb ) ;
goto out_free ;
}
list_for_each_entry ( snod , & sleb - > nodes , list ) {
switch ( snod - > type ) {
case UBIFS_REF_NODE : {
struct ubifs_ref_node * ref = snod - > node ;
int ref_lnum = le32_to_cpu ( ref - > lnum ) ;
err = done_already ( & done_tree , ref_lnum ) ;
if ( err < 0 )
goto out_scan ;
if ( err ! = 1 ) {
err = add_node ( c , buf , & write_lnum ,
& offs , snod - > node ) ;
if ( err )
goto out_scan ;
}
break ;
}
case UBIFS_CS_NODE :
if ( ! first )
break ;
err = add_node ( c , buf , & write_lnum , & offs ,
snod - > node ) ;
if ( err )
goto out_scan ;
first = 0 ;
break ;
}
}
ubifs_scan_destroy ( sleb ) ;
if ( lnum = = c - > lhead_lnum )
break ;
lnum = next_log_lnum ( c , lnum ) ;
}
if ( offs ) {
int sz = ALIGN ( offs , c - > min_io_size ) ;
ubifs_pad ( c , buf + offs , sz - offs ) ;
err = ubifs_leb_change ( c , write_lnum , buf , sz , UBI_SHORTTERM ) ;
if ( err )
goto out_free ;
offs = ALIGN ( offs , c - > min_io_size ) ;
}
destroy_done_tree ( & done_tree ) ;
vfree ( buf ) ;
if ( write_lnum = = c - > lhead_lnum ) {
ubifs_err ( " log is too full " ) ;
return - EINVAL ;
}
/* Unmap remaining LEBs */
lnum = write_lnum ;
do {
lnum = next_log_lnum ( c , lnum ) ;
err = ubifs_leb_unmap ( c , lnum ) ;
if ( err )
return err ;
} while ( lnum ! = c - > lhead_lnum ) ;
c - > lhead_lnum = write_lnum ;
c - > lhead_offs = offs ;
dbg_rcvry ( " new log head at %d:%d " , c - > lhead_lnum , c - > lhead_offs ) ;
return 0 ;
out_scan :
ubifs_scan_destroy ( sleb ) ;
out_free :
destroy_done_tree ( & done_tree ) ;
vfree ( buf ) ;
return err ;
}
# ifdef CONFIG_UBIFS_FS_DEBUG
/**
* dbg_check_bud_bytes - make sure bud bytes calculation are all right .
* @ c : UBIFS file - system description object
*
* This function makes sure the amount of flash space used by closed buds
* ( ' c - > bud_bytes ' is correct ) . Returns zero in case of success and % - EINVAL in
* case of failure .
*/
static int dbg_check_bud_bytes ( struct ubifs_info * c )
{
int i , err = 0 ;
struct ubifs_bud * bud ;
long long bud_bytes = 0 ;
if ( ! ( ubifs_chk_flags & UBIFS_CHK_GEN ) )
return 0 ;
spin_lock ( & c - > buds_lock ) ;
for ( i = 0 ; i < c - > jhead_cnt ; i + + )
list_for_each_entry ( bud , & c - > jheads [ i ] . buds_list , list )
bud_bytes + = c - > leb_size - bud - > start ;
if ( c - > bud_bytes ! = bud_bytes ) {
ubifs_err ( " bad bud_bytes %lld, calculated %lld " ,
c - > bud_bytes , bud_bytes ) ;
err = - EINVAL ;
}
spin_unlock ( & c - > buds_lock ) ;
return err ;
}
# endif /* CONFIG_UBIFS_FS_DEBUG */