2011-03-04 20:59:21 +01:00
/*
* Copyright ( C ) 2011 Andrea Mazzoleni
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* 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 , see < http : //www.gnu.org/licenses/>.
*/
# include "portable.h"
# include "util.h"
# include "elem.h"
2013-02-27 21:07:16 +01:00
# include "import.h"
2011-03-04 20:59:21 +01:00
# include "state.h"
# include "parity.h"
# include "handle.h"
2013-12-28 09:10:00 +01:00
# include "raid/raid.h"
2013-12-29 22:13:08 +01:00
# include "raid/combo.h"
2011-03-04 20:59:21 +01:00
/****************************************************************************/
/* check */
2012-02-09 23:17:36 +01:00
/**
* A block that failed the hash check , or that was deleted .
*/
2011-04-23 21:38:26 +02:00
struct failed_struct {
2013-02-24 11:10:10 +01:00
/**
* If the block needs to be recovered and rewritten to the disk .
*/
int is_bad ;
/**
* If that we have recovered may be not updated data ,
* an old version , or just garbage .
*
* Essentially , it means that we are not sure what we have recovered
* is really correct . It ' s just our best guess .
*
* These " recovered " block are also written to the disk if the block is marked as : : is_bad .
* But these files are marked also as FILE_IS_DAMAGED , and then renamed to . unrecoverable .
*
* Note that this could happen only for NEW and CHG blocks .
*/
int is_outofdate ;
2012-02-09 23:17:36 +01:00
unsigned index ; /**< Index of the failed block. */
struct snapraid_block * block ; /**< The failed block, or BLOCK_DELETED for a deleted block */
struct snapraid_handle * handle ; /**< The file containing the failed block, or 0 for a deleted block */
2011-04-23 21:38:26 +02:00
} ;
2011-04-28 21:38:25 +02:00
/**
* Checks if a block hash matches the specified buffer .
2013-02-24 19:02:53 +01:00
* Return = = 0 if equal
2011-04-28 21:38:25 +02:00
*/
2013-07-01 23:08:25 +02:00
static int blockcmp ( struct snapraid_state * state , int rehash , struct snapraid_block * block , unsigned char * buffer , unsigned char * buffer_zero )
2011-04-28 21:38:25 +02:00
{
unsigned char hash [ HASH_SIZE ] ;
unsigned size ;
size = block_file_size ( block , state - > block_size ) ;
/* now compute the hash of the valid part */
2013-07-01 23:08:25 +02:00
if ( rehash ) {
memhash ( state - > prevhash , state - > prevhashseed , hash , buffer , size ) ;
} else {
memhash ( state - > hash , state - > hashseed , hash , buffer , size ) ;
}
2011-04-28 21:38:25 +02:00
/* compare the hash */
if ( memcmp ( hash , block - > hash , HASH_SIZE ) ! = 0 ) {
return - 1 ;
}
/* compare to the end of the block */
if ( size < state - > block_size ) {
if ( memcmp ( buffer + size , buffer_zero + size , state - > block_size - size ) ! = 0 ) {
return - 1 ;
}
}
return 0 ;
}
2013-09-18 21:56:06 +02:00
/**
* Checks if the hash of at least one failed block is now matching .
*/
2013-12-27 13:56:56 +01:00
static int is_hash_matching ( struct snapraid_state * state , int rehash , unsigned diskmax , struct failed_struct * failed , unsigned * failed_map , unsigned failed_count , void * * buffer , void * buffer_zero )
2013-09-18 21:56:06 +02:00
{
unsigned j ;
int hash_checked ;
hash_checked = 0 ; /* keep track if we check at least one block */
/* check if the recovered blocks are OK */
for ( j = 0 ; j < failed_count ; + + j ) {
if ( block_has_updated_hash ( failed [ failed_map [ j ] ] . block ) ) { /* if the block can be checked */
hash_checked = 1 ;
if ( blockcmp ( state , rehash , failed [ failed_map [ j ] ] . block , buffer [ failed [ failed_map [ j ] ] . index ] , buffer_zero ) ! = 0 )
break ;
}
}
/* if we checked something, and no block failed the check */
if ( hash_checked & & j = = failed_count ) {
/* recompute all the redundancy information */
2013-12-28 15:43:00 +01:00
raid_par ( diskmax , state - > level , state - > block_size , buffer ) ;
2013-09-18 21:56:06 +02:00
return 1 ;
}
return 0 ;
}
/**
* Checks if specified parity is now matching with a recomputed one .
*/
2013-12-27 13:56:56 +01:00
static int is_parity_matching ( struct snapraid_state * state , unsigned diskmax , unsigned i , void * * buffer , void * * buffer_recov )
2013-09-18 21:56:06 +02:00
{
/* recompute parity, note that we don't need parity over i */
2013-12-28 15:43:00 +01:00
raid_par ( diskmax , i + 1 , state - > block_size , buffer ) ;
2013-09-18 21:56:06 +02:00
/* if the recovered parity block matches */
if ( memcmp ( buffer [ diskmax + i ] , buffer_recov [ i ] , state - > block_size ) = = 0 ) {
/* recompute all the redundancy information */
2013-12-28 15:43:00 +01:00
raid_par ( diskmax , state - > level , state - > block_size , buffer ) ;
2013-09-18 21:56:06 +02:00
return 1 ;
}
return 0 ;
}
2011-04-23 21:38:26 +02:00
/**
* Repair errors .
2011-10-01 19:18:32 +02:00
* Returns < 0 if failure for missing strategy , > 0 if data is wrong and we cannot rebuild correctly , 0 on success .
2013-10-25 18:03:05 +02:00
* If success , the parity are computed in the buffer variable .
2011-04-23 21:38:26 +02:00
*/
2013-12-27 13:56:56 +01:00
static int repair_step ( struct snapraid_state * state , int rehash , unsigned pos , unsigned diskmax , struct failed_struct * failed , unsigned * failed_map , unsigned failed_count , void * * buffer , void * * buffer_recov , void * buffer_zero )
2011-04-23 21:38:26 +02:00
{
2013-10-29 22:22:40 +01:00
unsigned i , n ;
2013-09-18 21:56:06 +02:00
int error ;
2012-02-09 23:17:36 +01:00
int has_hash ;
2013-12-26 09:45:51 +01:00
int id [ LEV_MAX ] ;
int ip [ LEV_MAX ] ;
2011-04-24 10:24:05 +02:00
2011-04-23 21:38:26 +02:00
/* no fix required */
if ( failed_count = = 0 ) {
2013-10-25 18:03:05 +02:00
/* recompute only the parity */
2013-12-28 15:43:00 +01:00
raid_par ( diskmax , state - > level , state - > block_size , buffer ) ;
2011-04-23 21:38:26 +02:00
return 0 ;
}
2013-10-29 22:22:40 +01:00
2013-10-25 18:03:05 +02:00
/* if too many error, we don't have any strategy */
2013-10-29 22:22:40 +01:00
n = state - > level ;
if ( failed_count > n ) {
2013-10-25 18:03:05 +02:00
return - 1 ;
}
2013-09-18 21:56:06 +02:00
error = 0 ;
2013-10-25 18:03:05 +02:00
/* setup vector of failed disk indexes */
for ( i = 0 ; i < failed_count ; + + i )
2013-12-26 09:45:51 +01:00
id [ i ] = failed [ failed_map [ i ] ] . index ;
2013-10-25 18:03:05 +02:00
2013-02-24 11:10:10 +01:00
/* check if there is at least a failed block that can be checked for correctness using the hash */
2012-06-20 23:05:15 +02:00
/* if there isn't, we have to sacrifice a parity block to check that the result is correct */
2012-02-09 23:17:36 +01:00
has_hash = 0 ;
2013-10-29 22:22:40 +01:00
for ( i = 0 ; i < failed_count ; + + i ) {
if ( block_has_updated_hash ( failed [ failed_map [ i ] ] . block ) ) /* if the block can be checked */
2012-02-09 23:17:36 +01:00
has_hash = 1 ;
}
2013-10-29 22:22:40 +01:00
/* if we don't have a hash, but we have an extra parity */
/* (strictly-less failures than number of parities) */
if ( ! has_hash & & failed_count < n ) {
/* number of parity to use, one more to check the recovering */
unsigned r = failed_count + 1 ;
/* all combinations (r of n) parities */
2013-12-26 09:45:51 +01:00
combination_first ( r , n , ip ) ;
2013-10-29 22:22:40 +01:00
do {
/* if a parity is missing, do nothing */
for ( i = 0 ; i < r ; + + i ) {
2013-12-26 09:45:51 +01:00
if ( buffer_recov [ ip [ i ] ] = = 0 )
2013-10-25 18:03:05 +02:00
break ;
}
2013-10-29 22:22:40 +01:00
if ( i ! = r )
2013-09-18 21:56:06 +02:00
continue ;
2013-09-11 23:30:32 +02:00
2013-10-29 22:22:40 +01:00
/* copy the parities to use, one less because the last is used for checking */
for ( i = 0 ; i < r - 1 ; + + i )
2013-12-26 09:45:51 +01:00
memcpy ( buffer [ diskmax + ip [ i ] ] , buffer_recov [ ip [ i ] ] , state - > block_size ) ;
2013-09-11 23:30:32 +02:00
2013-12-26 09:45:51 +01:00
/* recover using one less parity, the ip[r-1] one */
2013-12-28 15:12:09 +01:00
raid_rec_dataonly ( r - 1 , id , ip , diskmax , state - > block_size , buffer ) ;
2013-09-11 23:30:32 +02:00
2013-12-26 09:45:51 +01:00
/* use the remaining ip[r-1] parity to check the result */
if ( is_parity_matching ( state , diskmax , ip [ r - 1 ] , buffer , buffer_recov ) )
2013-09-18 21:56:06 +02:00
return 0 ;
2013-09-11 23:30:32 +02:00
2013-10-25 18:03:05 +02:00
/* log */
fprintf ( stdlog , " parity_error:%u: " , pos ) ;
2013-10-29 22:22:40 +01:00
for ( i = 0 ; i < r ; + + i ) {
if ( i ! = 0 )
2013-10-25 18:03:05 +02:00
fprintf ( stdlog , " / " ) ;
2013-12-26 09:45:51 +01:00
fprintf ( stdlog , " %s " , lev_config_name ( ip [ i ] ) ) ;
2013-10-25 18:03:05 +02:00
}
fprintf ( stdlog , " : Data error \n " ) ;
2013-09-18 21:56:06 +02:00
+ + error ;
2013-12-26 09:45:51 +01:00
} while ( combination_next ( r , n , ip ) ) ;
2013-09-11 23:30:32 +02:00
}
2013-10-29 22:22:40 +01:00
/* if we have a hash, and enough parities */
/* (less-or-equal failures than number of parities) */
if ( has_hash & & failed_count < = n ) {
/* number of parities to use equal at the number of failures */
unsigned r = failed_count ;
/* all combinations (r of n) parities */
2013-12-26 09:45:51 +01:00
combination_first ( r , n , ip ) ;
2013-10-29 22:22:40 +01:00
do {
/* if a parity is missing, do nothing */
for ( i = 0 ; i < r ; + + i ) {
2013-12-26 09:45:51 +01:00
if ( buffer_recov [ ip [ i ] ] = = 0 )
2013-10-25 18:03:05 +02:00
break ;
}
2013-10-29 22:22:40 +01:00
if ( i ! = r )
2013-09-18 21:56:06 +02:00
continue ;
2013-09-11 23:30:32 +02:00
2013-10-29 22:22:40 +01:00
/* copy the parities to use */
for ( i = 0 ; i < r ; + + i )
2013-12-26 09:45:51 +01:00
memcpy ( buffer [ diskmax + ip [ i ] ] , buffer_recov [ ip [ i ] ] , state - > block_size ) ;
2013-09-11 23:30:32 +02:00
2013-10-25 18:03:05 +02:00
/* recover */
2013-12-28 15:12:09 +01:00
raid_rec_dataonly ( r , id , ip , diskmax , state - > block_size , buffer ) ;
2013-09-11 23:30:32 +02:00
2013-10-25 18:03:05 +02:00
/* use the hash to check the result */
2013-09-18 21:56:06 +02:00
if ( is_hash_matching ( state , rehash , diskmax , failed , failed_map , failed_count , buffer , buffer_zero ) )
return 0 ;
2013-09-11 23:30:32 +02:00
2013-10-25 18:03:05 +02:00
/* log */
fprintf ( stdlog , " parity_error:%u: " , pos ) ;
2013-10-29 22:22:40 +01:00
for ( i = 0 ; i < r ; + + i ) {
if ( i ! = 0 )
2013-10-25 18:03:05 +02:00
fprintf ( stdlog , " / " ) ;
2013-12-26 09:45:51 +01:00
fprintf ( stdlog , " %s " , lev_config_name ( ip [ i ] ) ) ;
2013-10-25 18:03:05 +02:00
}
fprintf ( stdlog , " : Data error \n " ) ;
2013-09-18 21:56:06 +02:00
+ + error ;
2013-12-26 09:45:51 +01:00
} while ( combination_next ( r , n , ip ) ) ;
2013-09-11 23:30:32 +02:00
}
2012-02-09 23:17:36 +01:00
/* return the number of failed attempts, or -1 if no strategy */
if ( error )
return error ;
else
return - 1 ;
}
2013-12-27 13:56:56 +01:00
static int repair ( struct snapraid_state * state , int rehash , unsigned pos , unsigned diskmax , struct failed_struct * failed , unsigned * failed_map , unsigned failed_count , void * * buffer , void * * buffer_recov , void * buffer_zero )
2012-02-09 23:17:36 +01:00
{
int ret ;
int error ;
unsigned j ;
int n ;
2013-02-27 21:07:16 +01:00
int something_to_recover ;
2012-02-09 23:17:36 +01:00
error = 0 ;
2013-02-27 21:07:16 +01:00
/* Here we have to try two different strategies to recover, because in case the 'sync' */
/* process is aborted, we don't know if the parity data is really updated just like after 'sync', */
/* or if it still represents the state before the 'sync'. */
2012-06-20 23:05:15 +02:00
2013-02-27 21:07:16 +01:00
/* As first, we assume that the parity is already computed for the current state */
2012-06-20 23:05:15 +02:00
/* and that we are going to recover the state after the last 'sync'. */
2013-02-27 21:07:16 +01:00
/* This is the normal condition. */
/* In this case, parity contains correct info for BLK, CHG (new version) and NEW blocks, */
/* and not for DELETED ones. */
/* We need to put in the recovering process only the bad blocks, because all the */
/* others already contains the correct data read frin disk, and the parity is correctly computed for them. */
/* We are interested to recover BLK, CHG and NEW blocks if they are marked as bad, */
/* but we are not interested in DELETED ones. */
2012-02-09 23:17:36 +01:00
n = 0 ;
2013-02-27 21:07:16 +01:00
something_to_recover = 0 ; /* keep track if there is at least one block to fix */
2012-02-09 23:17:36 +01:00
for ( j = 0 ; j < failed_count ; + + j ) {
if ( failed [ j ] . is_bad ) {
2013-02-27 21:07:16 +01:00
unsigned block_state = block_state_get ( failed [ j ] . block ) ;
assert ( block_state ! = BLOCK_STATE_DELETED ) ; /* we cannot have bad DELETED blocks */
if ( block_state = = BLOCK_STATE_BLK ) { /* if we have the hash for it */
/* try to fetch the block using the hash */
2013-07-01 23:08:25 +02:00
if ( state_import_fetch ( state , rehash , failed [ j ] . block - > hash , buffer [ failed [ j ] . index ] ) = = 0 ) {
2013-02-27 21:07:16 +01:00
/* we have corrected it! */
} else {
/* otherwise try to recover it */
failed_map [ n ] = j ;
+ + n ;
/* we have something to try to recover */
something_to_recover = 1 ;
}
} else {
assert ( block_state = = BLOCK_STATE_CHG | | block_state = = BLOCK_STATE_NEW ) ;
2013-03-19 21:59:13 +01:00
2013-02-27 21:07:16 +01:00
/* otherwise it's CHG or NEW and we try to recover it */
failed_map [ n ] = j ;
+ + n ;
2013-03-19 21:59:13 +01:00
2013-02-27 21:07:16 +01:00
/* we have something to try to recover */
something_to_recover = 1 ;
}
2012-02-09 23:17:36 +01:00
}
}
/* if nothing to fix */
2013-02-27 21:07:16 +01:00
if ( ! something_to_recover ) {
2013-10-25 18:03:05 +02:00
/* recompute only the parity */
2013-12-28 15:43:00 +01:00
raid_par ( diskmax , state - > level , state - > block_size , buffer ) ;
2012-02-09 23:17:36 +01:00
return 0 ;
}
2013-09-15 21:12:41 +02:00
ret = repair_step ( state , rehash , pos , diskmax , failed , failed_map , n , buffer , buffer_recov , buffer_zero ) ;
2012-02-09 23:17:36 +01:00
if ( ret = = 0 ) {
2012-06-20 23:05:15 +02:00
/* reprocess the blocks for NEW and CHG ones, for which we don't have a hash to check */
/* if they were BAD we have to use some euristics to ensure that we have recovered */
/* the state after the sync. If unsure, we assume the worst case */
2012-02-09 23:17:36 +01:00
for ( j = 0 ; j < failed_count ; + + j ) {
/* we take care only of BAD blocks we have to write back */
if ( failed [ j ] . is_bad ) {
unsigned block_state = block_state_get ( failed [ j ] . block ) ;
2012-06-20 23:05:15 +02:00
2012-02-09 23:17:36 +01:00
if ( block_state = = BLOCK_STATE_NEW ) {
2013-02-24 19:02:53 +01:00
/* if the block is not filled with 0, we are sure to have */
/* restored it to the state after the 'sync' */
/* instead, if the block is filled with 0, it could be either that the */
2012-06-20 23:05:15 +02:00
/* block after the sync is really filled by 0, or that */
/* we restored the block before the 'sync'. */
2012-02-09 23:17:36 +01:00
if ( memcmp ( buffer [ failed [ j ] . index ] , buffer_zero , state - > block_size ) = = 0 ) {
/* it may contain garbage */
failed [ j ] . is_outofdate = 1 ;
}
2013-02-27 21:07:16 +01:00
} else if ( block_state = = BLOCK_STATE_CHG ) {
2013-02-24 19:02:53 +01:00
/* if the hash is a bogus value we cannot check the result */
/* this could happen if we have lost this information */
/* after an aborted sync */
if ( memcmp ( failed [ j ] . block - > hash , buffer_zero , HASH_SIZE ) = = 0 ) {
/* it may contain garbage */
failed [ j ] . is_outofdate = 1 ;
} else
2013-02-27 21:07:16 +01:00
/* if the hash is different than the previous one, we are sure to have */
2013-02-24 19:02:53 +01:00
/* restored it to the state after the 'sync' */
/* instead, if the hash matches, it could be either that the */
/* block after the sync has this hash, or that */
/* we restored the block before the 'sync'. */
2013-07-01 23:08:25 +02:00
if ( blockcmp ( state , rehash , failed [ j ] . block , buffer [ failed [ j ] . index ] , buffer_zero ) = = 0 ) {
2013-02-24 19:02:53 +01:00
/* it may contain garbage */
failed [ j ] . is_outofdate = 1 ;
}
2012-02-09 23:17:36 +01:00
}
}
}
2012-06-20 23:05:15 +02:00
2012-02-09 23:17:36 +01:00
return 0 ;
}
if ( ret > 0 )
error + = ret ;
2013-02-27 21:07:16 +01:00
/* Now assume that the parity computation was not updated at the current state, */
2012-02-09 23:17:36 +01:00
/* but still represent the state before the last 'sync' process. */
2013-02-27 21:07:16 +01:00
/* This may happen only if a 'sync' is aborted by a crash or manually. */
/* In this case, parity contains info for BLK, CHG (old version) and DELETED blocks, */
/* but not for CHG (new version) and NEW ones. */
/* We are interested to recover BLK ones marked as bad, */
/* but we are not interested to recover NEW and CHG (new version) blocks, even if marked as bad, */
/* because we don't have parity for them and it's just impossible, */
/* and we are not interested to recover DELETED ones. */
2012-02-09 23:17:36 +01:00
n = 0 ;
2013-02-27 21:07:16 +01:00
something_to_recover = 0 ; /* keep track if there is at least one block to fix */
2012-02-09 23:17:36 +01:00
for ( j = 0 ; j < failed_count ; + + j ) {
2013-02-27 21:07:16 +01:00
unsigned block_state = block_state_get ( failed [ j ] . block ) ;
2012-02-09 23:17:36 +01:00
2013-02-27 21:07:16 +01:00
if ( block_state = = BLOCK_STATE_DELETED
| | block_state = = BLOCK_STATE_CHG
2012-02-09 23:17:36 +01:00
) {
2013-02-27 21:07:16 +01:00
/* If the block is CHG or DELETED, we don't have the original content of block, */
/* and we must try to recover it. */
/* This apply to CHG blocks even if they are not marked bad, */
2013-08-21 22:04:10 +02:00
/* because the parity is computed with old content, and not with the new one. */
2013-02-27 21:07:16 +01:00
/* Note that this recovering is done just to make possible to recover any other BLK one, */
2013-08-21 22:04:10 +02:00
/* we are not really interested in DELETED and CHG (old version) ones. */
2013-02-27 21:07:16 +01:00
/* try to fetch the old block using the old hash */
2013-07-01 23:08:25 +02:00
if ( state_import_fetch ( state , rehash , failed [ j ] . block - > hash , buffer [ failed [ j ] . index ] ) = = 0 ) {
2013-02-27 21:07:16 +01:00
/* note that from now the buffer is definitively lost */
/* we can do this only because it's the last retry of recovering */
} else {
/* otherwise try to recover it */
failed_map [ n ] = j ;
+ + n ;
2012-02-09 23:17:36 +01:00
2013-02-27 21:07:16 +01:00
/* note that we don't set something_to_recover, because we are */
/* not really interested to recover *only* old blocks. */
}
/* mark that we have restored or we will restore an old state */
/* note that if the block is not marked as is_bad, */
/* we are not going to write it in the disk */
failed [ j ] . is_outofdate = 1 ;
} else if ( block_state = = BLOCK_STATE_NEW ) {
/* If the block is NEW, restore it to the original 0 as before the 'sync' */
/* Also in this case, we do this to just allow recovering of other BLK ones */
2012-02-09 23:17:36 +01:00
memset ( buffer [ failed [ j ] . index ] , 0 , state - > block_size ) ;
/* note that from now the buffer is definitively lost */
/* we can do this only because it's the last retry of recovering */
2012-03-15 22:58:04 +01:00
2012-02-09 23:17:36 +01:00
/* mark that we have restored an old state */
2013-02-24 11:10:10 +01:00
/* note that if the block is not marked as is_bad, */
/* we are not going to write it in the disk */
2012-02-09 23:17:36 +01:00
failed [ j ] . is_outofdate = 1 ;
2013-02-27 21:07:16 +01:00
} else if ( failed [ j ] . is_bad ) {
/* If the block is bad we don't know its content, and we try to recover it */
/* At this point, we can have only BLK ones */
2012-02-09 23:17:36 +01:00
2013-02-27 21:07:16 +01:00
assert ( block_state = = BLOCK_STATE_BLK ) ;
2012-02-09 23:17:36 +01:00
2013-02-27 21:07:16 +01:00
/* we have something we are interested to recover */
something_to_recover = 1 ;
/* we try to recover it */
failed_map [ n ] = j ;
+ + n ;
}
}
2012-06-20 23:05:15 +02:00
2013-02-27 21:07:16 +01:00
/* if nothing to fix, we just don't try */
if ( something_to_recover ) {
2013-09-15 21:12:41 +02:00
ret = repair_step ( state , rehash , pos , diskmax , failed , failed_map , n , buffer , buffer_recov , buffer_zero ) ;
2013-02-27 21:07:16 +01:00
if ( ret = = 0 ) {
/* we alreay marked as outdated NEW and CHG blocks, we don't need to do it again */
return 0 ;
2012-02-09 23:17:36 +01:00
}
2013-02-27 21:07:16 +01:00
if ( ret > 0 )
error + = ret ;
2012-02-09 23:17:36 +01:00
}
/* return the number of failed attempts, or -1 if no strategy */
2011-04-24 10:24:05 +02:00
if ( error )
return error ;
else
return - 1 ;
2011-04-23 21:38:26 +02:00
}
2013-09-15 21:12:41 +02:00
static int state_check_process ( struct snapraid_state * state , int check , int fix , struct snapraid_parity * * parity , block_off_t blockstart , block_off_t blockmax )
2011-03-04 20:59:21 +01:00
{
struct snapraid_handle * handle ;
2011-09-23 21:48:50 +02:00
unsigned diskmax ;
2011-03-12 10:09:59 +01:00
block_off_t i ;
2011-03-04 20:59:21 +01:00
unsigned j ;
2011-04-23 21:38:26 +02:00
void * buffer_alloc ;
2013-12-27 13:56:56 +01:00
void * * buffer ;
2011-04-23 21:38:26 +02:00
unsigned buffermax ;
2011-03-04 20:59:21 +01:00
int ret ;
2011-03-28 20:55:57 +02:00
data_off_t countsize ;
block_off_t countpos ;
block_off_t countmax ;
2011-03-04 20:59:21 +01:00
unsigned error ;
unsigned unrecoverable_error ;
unsigned recovered_error ;
2011-06-11 12:05:02 +02:00
struct failed_struct * failed ;
2012-11-02 14:07:18 +01:00
unsigned * failed_map ;
2013-09-15 21:12:41 +02:00
unsigned l ;
2011-03-04 20:59:21 +01:00
2011-09-23 21:48:50 +02:00
handle = handle_map ( state , & diskmax ) ;
2011-04-24 10:24:05 +02:00
/* we need disk + 2 for each parity level buffers + 1 zero buffer */
buffermax = diskmax + state - > level * 2 + 1 ;
2011-04-23 21:38:26 +02:00
2013-11-04 19:45:29 +01:00
buffer = malloc_nofail_vector_align ( diskmax , buffermax , state - > block_size , & buffer_alloc ) ;
2013-10-27 17:31:10 +01:00
if ( ! state - > opt . skip_self )
mtest_vector ( buffer , buffermax , state - > block_size ) ;
2013-09-05 22:29:58 +02:00
/* fill up the zero buffer */
2011-04-24 10:24:05 +02:00
memset ( buffer [ buffermax - 1 ] , 0 , state - > block_size ) ;
2013-12-27 13:56:56 +01:00
raid_zero ( buffer [ buffermax - 1 ] ) ;
2011-03-04 20:59:21 +01:00
2011-06-11 12:05:02 +02:00
failed = malloc_nofail ( diskmax * sizeof ( struct failed_struct ) ) ;
2012-11-02 14:07:18 +01:00
failed_map = malloc_nofail ( diskmax * sizeof ( unsigned ) ) ;
2011-06-11 12:05:02 +02:00
2011-03-04 20:59:21 +01:00
error = 0 ;
unrecoverable_error = 0 ;
recovered_error = 0 ;
2011-03-30 00:14:43 +02:00
/* first count the number of blocks to process */
countmax = 0 ;
for ( i = blockstart ; i < blockmax ; + + i ) {
int one_tocheck ;
/* for each disk */
one_tocheck = 0 ;
for ( j = 0 ; j < diskmax ; + + j ) {
2012-02-09 23:17:36 +01:00
struct snapraid_block * block = BLOCK_EMPTY ;
2011-10-01 19:18:32 +02:00
if ( handle [ j ] . disk )
block = disk_block_get ( handle [ j ] . disk , i ) ;
2013-02-26 20:26:57 +01:00
/* try to recover all files, even the ones without hash */
/* because in some cases we can recover also them */
if ( block_has_file ( block )
2011-04-28 23:09:19 +02:00
& & ! file_flag_has ( block_file_get ( block ) , FILE_IS_EXCLUDED ) /* only if the file is not filtered out */
2011-03-30 00:14:43 +02:00
) {
one_tocheck = 1 ;
break ;
}
}
/* if no block to check skip */
if ( ! one_tocheck )
continue ;
+ + countmax ;
}
2011-08-26 14:18:38 +02:00
/* check all the blocks in files */
2011-03-28 20:55:57 +02:00
countsize = 0 ;
countpos = 0 ;
2011-06-20 20:35:04 +02:00
state_progress_begin ( state , blockstart , blockmax , countmax ) ;
2011-03-04 20:59:21 +01:00
for ( i = blockstart ; i < blockmax ; + + i ) {
2011-04-23 21:38:26 +02:00
unsigned failed_count ;
2011-03-30 00:14:43 +02:00
int one_tocheck ;
2013-02-24 19:43:53 +01:00
int valid_parity ;
2013-07-01 23:08:25 +02:00
snapraid_info info ;
int rehash ;
2011-03-04 20:59:21 +01:00
2011-03-22 23:23:45 +01:00
/* for each disk */
2011-03-30 00:14:43 +02:00
one_tocheck = 0 ;
2011-03-04 20:59:21 +01:00
for ( j = 0 ; j < diskmax ; + + j ) {
2012-02-09 23:17:36 +01:00
struct snapraid_block * block = BLOCK_EMPTY ;
2011-10-01 19:18:32 +02:00
if ( handle [ j ] . disk )
block = disk_block_get ( handle [ j ] . disk , i ) ;
2013-02-26 20:26:57 +01:00
/* try to recover all files, even the ones without hash */
/* because in some cases we can recover also them */
if ( block_has_file ( block )
2011-04-28 23:09:19 +02:00
& & ! file_flag_has ( block_file_get ( block ) , FILE_IS_EXCLUDED ) /* only if the file is not filtered out */
2011-03-30 00:14:43 +02:00
) {
one_tocheck = 1 ;
2011-03-23 18:54:08 +01:00
break ;
2011-03-04 20:59:21 +01:00
}
}
2011-03-30 00:14:43 +02:00
/* if no block to check skip */
if ( ! one_tocheck )
2011-03-04 20:59:21 +01:00
continue ;
2013-02-24 19:43:53 +01:00
/* If we have valid parity, and it makes sense to check its content. */
/* If we already know that the parity is invalid, we just read the file */
/* but we don't report errors */
2012-10-07 23:18:10 +02:00
/* Note that if check==0, we'll anyway skip the full parity check, */
/* because we also don't read it at all */
2013-02-24 19:43:53 +01:00
valid_parity = 1 ;
2012-10-07 23:18:10 +02:00
/* keep track of the number of failed blocks */
failed_count = 0 ;
2011-03-23 18:54:08 +01:00
2013-10-25 18:03:05 +02:00
/* get block specific info */
2013-07-01 23:08:25 +02:00
info = info_get ( & state - > infoarr , i ) ;
2013-10-25 18:03:05 +02:00
/* if we have to use the old hash */
2013-07-01 23:08:25 +02:00
rehash = info_get_rehash ( info ) ;
2011-03-23 18:54:08 +01:00
/* for each disk, process the block */
2011-03-04 20:59:21 +01:00
for ( j = 0 ; j < diskmax ; + + j ) {
int read_size ;
2011-04-12 22:16:35 +02:00
unsigned char hash [ HASH_SIZE ] ;
2011-03-04 20:59:21 +01:00
struct snapraid_block * block ;
2012-02-09 23:17:36 +01:00
unsigned block_state ;
2011-03-04 20:59:21 +01:00
2011-10-01 19:18:32 +02:00
/* if the disk position is not used */
if ( ! handle [ j ] . disk ) {
/* use an empty block */
memset ( buffer [ j ] , 0 , state - > block_size ) ;
continue ;
}
/* if the disk block is not used */
2011-03-04 20:59:21 +01:00
block = disk_block_get ( handle [ j ] . disk , i ) ;
2012-02-09 23:17:36 +01:00
if ( block = = BLOCK_EMPTY ) {
2011-04-23 21:38:26 +02:00
/* use an empty block */
memset ( buffer [ j ] , 0 , state - > block_size ) ;
2011-03-04 20:59:21 +01:00
continue ;
2011-04-23 21:38:26 +02:00
}
2011-03-04 20:59:21 +01:00
2013-02-24 19:02:53 +01:00
/* get the state of the block */
block_state = block_state_get ( block ) ;
2013-02-24 19:43:53 +01:00
/* if the parity is not valid */
if ( block_has_invalid_parity ( block ) ) {
/* mark the parity as invalid, and don't try to check/fix it */
/* because it will be recomputed at the next sync */
valid_parity = 0 ;
}
2012-02-09 23:17:36 +01:00
/* if the block is deleted */
2013-02-24 19:02:53 +01:00
if ( block_state = = BLOCK_STATE_DELETED ) {
2011-04-23 21:38:26 +02:00
/* use an empty block */
memset ( buffer [ j ] , 0 , state - > block_size ) ;
2013-02-24 19:02:53 +01:00
2012-02-09 23:17:36 +01:00
/* store it in the failed set, because potentially */
/* the parity may be still computed with the previous content */
failed [ failed_count ] . is_bad = 0 ;
failed [ failed_count ] . is_outofdate = 0 ;
failed [ failed_count ] . index = j ;
2013-02-24 19:02:53 +01:00
failed [ failed_count ] . block = block ;
2012-02-09 23:17:36 +01:00
failed [ failed_count ] . handle = 0 ;
+ + failed_count ;
2011-03-04 20:59:21 +01:00
continue ;
2011-04-23 21:38:26 +02:00
}
2011-03-04 20:59:21 +01:00
2012-10-07 23:18:10 +02:00
/* if we are only hashing, we can skip excluded files and don't event read them */
if ( ! check & & file_flag_has ( block_file_get ( block ) , FILE_IS_EXCLUDED ) ) {
/* use an empty block */
/* in true, this is unnecessary, becase we are not checking any parity */
/* but we keep it for completeness */
memset ( buffer [ j ] , 0 , state - > block_size ) ;
continue ;
}
2013-04-28 19:01:36 +02:00
/* if the file is closed or different than the current one */
if ( handle [ j ] . file = = 0 | | handle [ j ] . file ! = block_file_get ( block ) ) {
/* close the old one, if any */
2011-08-31 23:33:43 +02:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
2013-06-29 17:53:40 +02:00
fprintf ( stderr , " DANGER! Unexpected close error in a data disk, it isn't possible to check. \n " ) ;
2011-08-31 23:33:43 +02:00
printf ( " Stopping at block %u \n " , i ) ;
+ + unrecoverable_error ;
goto bail ;
}
2011-03-18 22:38:11 +01:00
2013-04-28 19:01:36 +02:00
/* if fixing, and the file is not excluded, we must open for writing */
if ( fix & & ! file_flag_has ( block_file_get ( block ) , FILE_IS_EXCLUDED ) ) {
/* if fixing, create the file, open for writing and resize if required */
2013-06-29 18:11:36 +02:00
ret = handle_create ( & handle [ j ] , block_file_get ( block ) , state - > opt . skip_sequential ) ;
2013-04-28 19:01:36 +02:00
if ( ret = = - 1 ) {
if ( errno = = EACCES ) {
fprintf ( stderr , " WARNING! Please give write permission to the file. \n " ) ;
} else {
fprintf ( stderr , " DANGER! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
}
printf ( " Stopping at block %u \n " , i ) ;
+ + unrecoverable_error ;
goto bail ;
2011-06-11 09:38:19 +02:00
}
2011-04-28 21:38:25 +02:00
2013-04-28 19:01:36 +02:00
/* check if the file was larger and now truncated */
2013-06-01 22:49:38 +02:00
if ( handle [ j ] . truncated ! = 0 ) {
2013-04-28 19:01:36 +02:00
fprintf ( stdlog , " File '%s' is larger than expected. \n " , handle [ j ] . path ) ;
fprintf ( stdlog , " error:%u:%s:%s: Size error \n " , i , handle [ j ] . disk - > name , block_file_get ( block ) - > sub ) ;
+ + error ;
2011-06-12 14:18:36 +02:00
2013-04-28 19:01:36 +02:00
/* this is already a recovered error */
fprintf ( stdlog , " fixed:%u:%s:%s: Fixed size \n " , i , handle [ j ] . disk - > name , block_file_get ( block ) - > sub ) ;
+ + recovered_error ;
}
2011-03-04 20:59:21 +01:00
2013-04-28 19:01:36 +02:00
/* check if the file was just created */
if ( handle [ j ] . created ! = 0 ) {
/* if fragmented, it may be reopened, so remember that the file */
/* was originally missing */
file_flag_set ( block_file_get ( block ) , FILE_IS_CREATED ) ;
}
} else {
/* if checking or hashing, open the file only for reading */
2013-09-21 19:11:11 +02:00
ret = handle_open ( & handle [ j ] , block_file_get ( block ) , state - > opt . skip_sequential , stdlog ) ;
2013-04-28 19:01:36 +02:00
if ( ret = = - 1 ) {
/* save the failed block for the check/fix */
failed [ failed_count ] . is_bad = 1 ;
failed [ failed_count ] . is_outofdate = 0 ;
failed [ failed_count ] . index = j ;
failed [ failed_count ] . block = block ;
failed [ failed_count ] . handle = & handle [ j ] ;
+ + failed_count ;
fprintf ( stdlog , " error:%u:%s:%s: Open error at position %u \n " , i , handle [ j ] . disk - > name , block_file_get ( block ) - > sub , block_file_pos ( block ) ) ;
+ + error ;
continue ;
}
2011-04-28 21:38:25 +02:00
2013-04-28 19:01:36 +02:00
/* check if it's a larger file, but not if already notified */
if ( ! file_flag_has ( block_file_get ( block ) , FILE_IS_LARGER )
& & handle [ j ] . st . st_size > block_file_get ( block ) - > size
) {
fprintf ( stdlog , " File '%s' is larger than expected. \n " , handle [ j ] . path ) ;
fprintf ( stdlog , " error:%u:%s:%s: Size error \n " , i , handle [ j ] . disk - > name , block_file_get ( block ) - > sub ) ;
+ + error ;
2011-04-28 21:38:25 +02:00
2013-04-28 19:01:36 +02:00
/* if fragmented, it may be reopened, so store the notification */
/* to prevent to signal and count the error more than one time */
file_flag_set ( block_file_get ( block ) , FILE_IS_LARGER ) ;
}
2011-04-28 21:38:25 +02:00
}
2011-03-04 20:59:21 +01:00
}
2012-10-07 23:18:10 +02:00
/* read from the file */
2013-03-01 20:13:56 +01:00
read_size = handle_read ( & handle [ j ] , block , buffer [ j ] , state - > block_size , stdlog ) ;
2011-03-04 20:59:21 +01:00
if ( read_size = = - 1 ) {
2011-06-11 12:05:02 +02:00
/* save the failed block for the check/fix */
2012-02-09 23:17:36 +01:00
failed [ failed_count ] . is_bad = 1 ;
failed [ failed_count ] . is_outofdate = 0 ;
2011-06-11 12:05:02 +02:00
failed [ failed_count ] . index = j ;
failed [ failed_count ] . block = block ;
failed [ failed_count ] . handle = & handle [ j ] ;
2011-04-23 21:38:26 +02:00
+ + failed_count ;
2011-03-04 20:59:21 +01:00
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " error:%u:%s:%s: Read error at position %u \n " , i , handle [ j ] . disk - > name , block_file_get ( block ) - > sub , block_file_pos ( block ) ) ;
2011-03-04 20:59:21 +01:00
+ + error ;
continue ;
}
2012-02-09 23:17:36 +01:00
countsize + = read_size ;
2011-03-04 20:59:21 +01:00
2012-02-09 23:17:36 +01:00
/* if the block is potentially not updated */
if ( block_state = = BLOCK_STATE_NEW | | block_state = = BLOCK_STATE_CHG ) {
/* store it in the failed set, because potentially */
/* the parity may be still computed with the previous content */
failed [ failed_count ] . is_bad = 0 ;
failed [ failed_count ] . is_outofdate = 0 ;
2011-06-11 12:05:02 +02:00
failed [ failed_count ] . index = j ;
failed [ failed_count ] . block = block ;
failed [ failed_count ] . handle = & handle [ j ] ;
2011-04-23 21:38:26 +02:00
+ + failed_count ;
2011-03-04 20:59:21 +01:00
continue ;
}
2012-02-09 23:17:36 +01:00
/* if the block has the hash */
2013-02-24 19:43:53 +01:00
if ( block_state = = BLOCK_STATE_BLK ) {
2012-10-07 23:18:10 +02:00
/* compute the hash of the block just read */
2013-07-01 23:08:25 +02:00
if ( rehash ) {
memhash ( state - > prevhash , state - > prevhashseed , hash , buffer [ j ] , read_size ) ;
} else {
memhash ( state - > hash , state - > hashseed , hash , buffer [ j ] , read_size ) ;
}
2012-02-09 23:17:36 +01:00
/* compare the hash */
if ( memcmp ( hash , block - > hash , HASH_SIZE ) ! = 0 ) {
/* save the failed block for the check/fix */
failed [ failed_count ] . is_bad = 1 ;
failed [ failed_count ] . is_outofdate = 0 ;
failed [ failed_count ] . index = j ;
failed [ failed_count ] . block = block ;
failed [ failed_count ] . handle = & handle [ j ] ;
+ + failed_count ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " error:%u:%s:%s: Data error at position %u \n " , i , handle [ j ] . disk - > name , block_file_get ( block ) - > sub , block_file_pos ( block ) ) ;
2012-02-09 23:17:36 +01:00
+ + error ;
continue ;
}
}
2011-03-04 20:59:21 +01:00
}
2012-10-07 23:18:10 +02:00
/* now read and check the parity if requested */
if ( check ) {
2013-12-27 13:56:56 +01:00
void * buffer_recov [ LEV_MAX ] ;
void * buffer_zero ;
2011-05-05 21:12:42 +02:00
2012-10-07 23:18:10 +02:00
/* buffers for parity read and not computed */
2013-09-18 21:56:06 +02:00
for ( l = 0 ; l < state - > level ; + + l )
buffer_recov [ l ] = buffer [ diskmax + state - > level + l ] ;
for ( ; l < LEV_MAX ; + + l )
buffer_recov [ l ] = 0 ;
/* the zero buffer is the last one */
2012-10-07 23:18:10 +02:00
buffer_zero = buffer [ buffermax - 1 ] ;
2011-04-24 10:24:05 +02:00
2012-10-07 23:18:10 +02:00
/* read the parity */
2013-09-15 21:12:41 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
if ( parity [ l ] ) {
ret = parity_read ( parity [ l ] , i , buffer_recov [ l ] , state - > block_size , stdlog ) ;
2013-09-11 23:30:32 +02:00
if ( ret = = - 1 ) {
2013-09-15 21:12:41 +02:00
buffer_recov [ l ] = 0 ; /* no parity to use */
2013-09-11 23:30:32 +02:00
2013-10-21 21:56:53 +02:00
fprintf ( stdlog , " parity_error:%u:%s: Read error \n " , i , lev_config_name ( l ) ) ;
2013-09-11 23:30:32 +02:00
+ + error ;
}
} else {
2013-09-15 21:12:41 +02:00
buffer_recov [ l ] = 0 ;
2013-09-11 23:30:32 +02:00
}
}
2013-02-24 11:10:10 +01:00
/* try all the recovering strategies */
2013-09-15 21:12:41 +02:00
ret = repair ( state , rehash , i , diskmax , failed , failed_map , failed_count , buffer , buffer_recov , buffer_zero ) ;
2012-10-07 23:18:10 +02:00
if ( ret ! = 0 ) {
/* increment the number of errors */
if ( ret > 0 )
error + = ret ;
+ + unrecoverable_error ;
/* print a list of all the errors in files */
2011-08-31 23:33:43 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2012-02-09 23:17:36 +01:00
if ( failed [ j ] . is_bad )
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " unrecoverable:%u:%s:%s: Unrecoverable error at position %u \n " , i , failed [ j ] . handle - > disk - > name , block_file_get ( failed [ j ] . block ) - > sub , block_file_pos ( failed [ j ] . block ) ) ;
2012-02-09 23:17:36 +01:00
}
2013-03-02 23:49:29 +01:00
/* keep track of damaged files */
for ( j = 0 ; j < failed_count ; + + j ) {
if ( failed [ j ] . is_bad )
file_flag_set ( block_file_get ( failed [ j ] . block ) , FILE_IS_DAMAGED ) ;
2011-03-04 20:59:21 +01:00
}
2012-10-07 23:18:10 +02:00
} else {
2013-02-24 11:10:10 +01:00
/* now counts partial recovers */
2012-10-07 23:18:10 +02:00
/* note that this could happen only when we have an incomplete 'sync' */
/* and that we have recovered is the state before the 'sync' */
int partial_recover_error = 0 ;
2011-03-04 20:59:21 +01:00
2012-10-07 23:18:10 +02:00
/* print a list of all the errors in files */
for ( j = 0 ; j < failed_count ; + + j ) {
if ( failed [ j ] . is_bad & & failed [ j ] . is_outofdate ) {
+ + partial_recover_error ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " unrecoverable:%u:%s:%s: Unrecoverable error at position %u \n " , i , failed [ j ] . handle - > disk - > name , block_file_get ( failed [ j ] . block ) - > sub , block_file_pos ( failed [ j ] . block ) ) ;
2011-04-23 21:38:26 +02:00
}
2011-03-18 22:38:11 +01:00
}
2012-10-07 23:18:10 +02:00
if ( partial_recover_error ! = 0 ) {
error + = partial_recover_error ;
+ + unrecoverable_error ;
}
2011-03-04 20:59:21 +01:00
2013-11-17 14:12:57 +01:00
/* now check parities, but only if all the blocks have it computed */
2012-10-07 23:18:10 +02:00
/* if you check/fix after a partial sync, it's OK to have parity errors on the blocks with invalid parity */
2013-02-24 11:10:10 +01:00
/* and doesn't make sense to try to fix it */
2013-02-24 19:43:53 +01:00
if ( valid_parity ) {
2012-10-07 23:18:10 +02:00
/* check the parity */
2013-09-15 21:12:41 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
if ( buffer_recov [ l ] ! = 0 & & memcmp ( buffer_recov [ l ] , buffer [ diskmax + l ] , state - > block_size ) ! = 0 ) {
buffer_recov [ l ] = 0 ;
2013-09-11 23:30:32 +02:00
2013-10-21 21:56:53 +02:00
fprintf ( stdlog , " parity_error:%u:%s: Data error \n " , i , lev_config_name ( l ) ) ;
2013-09-11 23:30:32 +02:00
+ + error ;
}
}
2012-10-07 23:18:10 +02:00
}
2012-02-09 23:17:36 +01:00
2013-02-24 11:10:10 +01:00
/* now writes recovered files */
2012-10-07 23:18:10 +02:00
if ( fix ) {
/* update the fixed files */
for ( j = 0 ; j < failed_count ; + + j ) {
/* nothing to do if it doesn't need recovering */
if ( ! failed [ j ] . is_bad )
continue ;
2011-08-31 23:33:43 +02:00
2012-10-07 23:18:10 +02:00
/* do not fix if the file filtered out */
if ( file_flag_has ( block_file_get ( failed [ j ] . block ) , FILE_IS_EXCLUDED ) )
continue ;
2011-05-05 21:12:42 +02:00
2012-10-07 23:18:10 +02:00
ret = handle_write ( failed [ j ] . handle , failed [ j ] . block , buffer [ failed [ j ] . index ] , state - > block_size ) ;
2011-04-23 21:38:26 +02:00
if ( ret = = - 1 ) {
2012-10-07 23:18:10 +02:00
/* mark the file as damaged */
file_flag_set ( block_file_get ( failed [ j ] . block ) , FILE_IS_DAMAGED ) ;
if ( errno = = EACCES ) {
fprintf ( stderr , " WARNING! Please give write permission to the file. \n " ) ;
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
}
2011-06-11 12:05:02 +02:00
printf ( " Stopping at block %u \n " , i ) ;
2011-04-23 21:38:26 +02:00
+ + unrecoverable_error ;
goto bail ;
}
2012-10-07 23:18:10 +02:00
/* if we are not sure that the recovered content is uptodate */
if ( failed [ j ] . is_outofdate ) {
/* mark the file as damaged */
file_flag_set ( block_file_get ( failed [ j ] . block ) , FILE_IS_DAMAGED ) ;
continue ;
}
/* mark the file as fixed */
file_flag_set ( block_file_get ( failed [ j ] . block ) , FILE_IS_FIXED ) ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " fixed:%u:%s:%s: Fixed data error at position %u \n " , i , failed [ j ] . handle - > disk - > name , block_file_get ( failed [ j ] . block ) - > sub , block_file_pos ( failed [ j ] . block ) ) ;
2011-04-23 21:38:26 +02:00
+ + recovered_error ;
}
2013-09-15 21:12:41 +02:00
/* update parity only if all the blocks have it computed */
2012-10-07 23:18:10 +02:00
/* if you check/fix after a partial sync, you do not want to fix parity */
/* for blocks that are going to have it computed in the sync completion */
2013-02-24 19:43:53 +01:00
if ( valid_parity ) {
2012-10-07 23:18:10 +02:00
/* update the parity */
2013-09-15 21:12:41 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
if ( buffer_recov [ l ] = = 0 & & parity [ l ] ! = 0 ) {
ret = parity_write ( parity [ l ] , i , buffer [ diskmax + l ] , state - > block_size ) ;
2013-09-11 23:30:32 +02:00
if ( ret = = - 1 ) {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
2013-09-15 21:12:41 +02:00
fprintf ( stderr , " WARNING! Without a working %s disk, it isn't possible to fix errors on it. \n " , lev_name ( l ) ) ;
2013-09-11 23:30:32 +02:00
printf ( " Stopping at block %u \n " , i ) ;
+ + unrecoverable_error ;
goto bail ;
}
2013-10-21 21:56:53 +02:00
fprintf ( stdlog , " parity_fixed:%u:%s: Fixed data error \n " , i , lev_config_name ( l ) ) ;
2013-09-11 23:30:32 +02:00
+ + recovered_error ;
}
}
2011-04-23 21:38:26 +02:00
}
2013-03-02 23:49:29 +01:00
} else {
/* if we are not fixing, we just set the FIXED flag */
for ( j = 0 ; j < failed_count ; + + j ) {
if ( failed [ j ] . is_bad ) {
file_flag_set ( block_file_get ( failed [ j ] . block ) , FILE_IS_FIXED ) ;
}
}
}
}
} else {
/* if we are not checking, we just set the DAMAGED flag */
for ( j = 0 ; j < failed_count ; + + j ) {
if ( failed [ j ] . is_bad ) {
file_flag_set ( block_file_get ( failed [ j ] . block ) , FILE_IS_DAMAGED ) ;
2011-04-23 21:38:26 +02:00
}
2011-03-04 20:59:21 +01:00
}
}
2013-04-28 19:01:36 +02:00
/* for all the files prints the final status, and does the final time fix */
/* we also ensure to close files after processing the last block */
2013-03-02 23:17:55 +01:00
for ( j = 0 ; j < diskmax ; + + j ) {
struct snapraid_block * block = BLOCK_EMPTY ;
struct snapraid_file * collide_file ;
struct snapraid_file * file ;
char path [ PATH_MAX ] ;
uint64_t inode ;
2011-08-31 23:33:43 +02:00
2013-03-02 23:17:55 +01:00
if ( handle [ j ] . disk )
block = disk_block_get ( handle [ j ] . disk , i ) ;
2013-03-01 00:12:57 +01:00
2013-03-02 23:17:55 +01:00
if ( ! block_has_file ( block ) ) {
/* if no file, nothing to do */
continue ;
}
file = block_file_get ( block ) ;
pathprint ( path , sizeof ( path ) , " %s%s " , handle [ j ] . disk - > dir , file - > sub ) ;
/* if the file is open, it must be the correct block one */
2013-03-02 23:49:29 +01:00
/* note tha if the file is excluded, it's also possible to have it not opened, */
/* and have the handle[j].file pointing to NULL. */
/* A typical case is if the file is missing, */
/* and the read-only open failed before. */
2013-03-02 23:17:55 +01:00
if ( handle [ j ] . file ! = 0 & & handle [ j ] . file ! = file ) {
fprintf ( stderr , " Internal inconsistency in opened file for block %u \n " , block - > parity_pos ) ;
exit ( EXIT_FAILURE ) ;
}
/* if it isn't the last block in the file */
if ( ! block_is_last ( block ) ) {
/* nothing to do */
continue ;
}
2013-04-28 19:01:36 +02:00
/* if the file is excluded, we have nothing to fix */
if ( file_flag_has ( file , FILE_IS_EXCLUDED ) ) {
/* nothing to do, but close the file */
goto close_and_continue ;
}
2013-03-02 23:17:55 +01:00
/* finish the fix process if it's the last block of the files */
if ( fix ) {
2013-05-31 23:34:59 +02:00
/* mark that we finished with this file */
file_flag_set ( file , FILE_IS_FINISHED ) ;
2011-10-10 20:42:52 +02:00
/* if the file is damaged, meaning that a fix failed */
2013-03-02 23:17:55 +01:00
if ( file_flag_has ( file , FILE_IS_DAMAGED ) ) {
2013-02-24 11:10:10 +01:00
/* rename it to .unrecoverable */
2011-10-10 20:42:52 +02:00
char path_to [ PATH_MAX ] ;
2013-03-02 23:17:55 +01:00
pathprint ( path_to , sizeof ( path_to ) , " %s%s.unrecoverable " , handle [ j ] . disk - > dir , file - > sub ) ;
2011-10-10 20:42:52 +02:00
2013-04-28 19:01:36 +02:00
/* ensure to close the file before renaming */
2012-09-19 18:30:23 +02:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret ! = 0 ) {
2013-03-02 23:17:55 +01:00
fprintf ( stderr , " Error closing '%s'. %s. \n " , path , strerror ( errno ) ) ;
2012-09-19 18:30:23 +02:00
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping at block %u \n " , i ) ;
+ + unrecoverable_error ;
goto bail ;
}
2013-03-02 23:17:55 +01:00
ret = rename ( path , path_to ) ;
2011-10-10 20:42:52 +02:00
if ( ret ! = 0 ) {
2013-03-02 23:17:55 +01:00
fprintf ( stderr , " Error renaming '%s' to '%s'. %s. \n " , path , path_to , strerror ( errno ) ) ;
2011-10-10 20:42:52 +02:00
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping at block %u \n " , i ) ;
+ + unrecoverable_error ;
goto bail ;
}
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " status:unrecoverable:%s:%s \n " , handle [ j ] . disk - > name , file - > sub ) ;
2013-10-31 18:41:10 +01:00
if ( ! state - > opt . quiet ) {
2013-10-30 22:59:33 +01:00
printf ( " unrecoverable %s \n " , path ) ;
}
2013-03-01 20:13:56 +01:00
2013-02-24 11:10:10 +01:00
/* and do not set the time if damaged */
2011-10-10 20:42:52 +02:00
continue ;
}
2013-02-24 11:10:10 +01:00
/* if the file is not fixed, meaning that it is untouched */
2013-03-02 23:17:55 +01:00
if ( ! file_flag_has ( file , FILE_IS_FIXED ) ) {
2013-04-28 19:01:36 +02:00
/* nothing to do, but close the file */
goto close_and_continue ;
2011-08-31 23:33:43 +02:00
}
2013-03-01 20:13:56 +01:00
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " status:recovered:%s:%s \n " , handle [ j ] . disk - > name , file - > sub ) ;
2013-10-31 18:41:10 +01:00
if ( ! state - > opt . quiet ) {
2013-10-30 22:59:33 +01:00
printf ( " recovered %s \n " , path ) ;
}
2013-03-01 20:13:56 +01:00
2011-08-31 23:33:43 +02:00
inode = handle [ j ] . st . st_ino ;
/* search for the corresponding inode */
2013-06-25 22:41:10 +02:00
collide_file = tommy_hashdyn_search ( & handle [ j ] . disk - > inodeset , file_inode_compare_to_arg , & inode , file_inode_hash ( inode ) ) ;
2011-08-31 23:33:43 +02:00
2013-02-24 11:10:10 +01:00
/* if the inode is already in the database and it refers at a different file name, */
2013-08-26 20:20:36 +02:00
/* we can fix the file time ONLY if the time and size allow to differentiate */
2011-08-31 23:33:43 +02:00
/* between the two files */
/* for example, suppose we delete a bunch of files with all the same size and time, */
/* when recreating them the inodes may be reused in a different order, */
/* and at the next sync some files may have matching inode/size/time even if different name */
/* not allowing sync to detect that the file is changed and not renamed */
2012-02-09 23:17:36 +01:00
if ( ! collide_file /* if not in the database, there is no collision */
2013-03-02 23:17:55 +01:00
| | strcmp ( collide_file - > sub , file - > sub ) = = 0 /* if the name is the same, it's the right collision */
| | collide_file - > size ! = file - > size /* if the size is different, the collision is identified */
| | collide_file - > mtime_sec ! = file - > mtime_sec /* if the mtime is different, the collision is identified */
2013-08-26 20:20:36 +02:00
| | collide_file - > mtime_nsec ! = file - > mtime_nsec /* same for mtime_nsec */
2011-08-31 23:33:43 +02:00
) {
/* set the original modification time */
ret = handle_utime ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
/* mark the file as damaged */
2013-03-02 23:17:55 +01:00
file_flag_set ( file , FILE_IS_DAMAGED ) ;
2011-08-31 23:33:43 +02:00
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping at block %u \n " , i ) ;
+ + unrecoverable_error ;
goto bail ;
}
} else {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " collision:%s:%s:%s: Not setting modification time to avoid inode collision \n " , handle [ j ] . disk - > name , block_file_get ( block ) - > sub , collide_file - > sub ) ;
2011-08-31 23:33:43 +02:00
}
2013-03-02 23:17:55 +01:00
} else {
/* we are not fixing, but only checking */
/* print just the final status */
if ( file_flag_has ( file , FILE_IS_DAMAGED ) ) {
2013-03-02 23:49:29 +01:00
if ( ! check ) {
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " status:damaged:%s:%s \n " , handle [ j ] . disk - > name , file - > sub ) ;
2013-10-31 18:41:10 +01:00
if ( ! state - > opt . quiet ) {
2013-10-30 22:59:33 +01:00
printf ( " damaged %s \n " , path ) ;
}
2013-03-02 23:17:55 +01:00
} else {
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " status:unrecoverable:%s:%s \n " , handle [ j ] . disk - > name , file - > sub ) ;
2013-10-31 18:41:10 +01:00
if ( ! state - > opt . quiet ) {
2013-10-30 22:59:33 +01:00
printf ( " unrecoverable %s \n " , path ) ;
}
2013-03-02 23:17:55 +01:00
}
} else if ( file_flag_has ( file , FILE_IS_FIXED ) ) {
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " status:recoverable:%s:%s \n " , handle [ j ] . disk - > name , file - > sub ) ;
2013-10-31 18:41:10 +01:00
if ( ! state - > opt . quiet ) {
2013-10-30 22:59:33 +01:00
printf ( " recoverable %s \n " , path ) ;
}
2013-03-02 23:17:55 +01:00
} else {
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " status:correct:%s:%s \n " , handle [ j ] . disk - > name , file - > sub ) ;
if ( state - > opt . verbose ) {
2013-10-19 13:24:35 +02:00
printf ( " correct %s \n " , path ) ;
2013-03-02 23:17:55 +01:00
}
}
2011-08-31 23:33:43 +02:00
}
2013-04-28 19:01:36 +02:00
close_and_continue :
/* ensure to close the file just after finishing with it */
ret = handle_close ( & handle [ j ] ) ;
if ( ret ! = 0 ) {
fprintf ( stderr , " Error closing '%s'. %s. \n " , path , strerror ( errno ) ) ;
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping at block %u \n " , i ) ;
+ + unrecoverable_error ;
goto bail ;
}
2011-08-31 23:33:43 +02:00
}
2011-03-04 20:59:21 +01:00
/* count the number of processed block */
2011-03-28 20:55:57 +02:00
+ + countpos ;
2011-03-04 20:59:21 +01:00
/* progress */
2011-06-20 20:35:04 +02:00
if ( state_progress ( state , i , countpos , countmax , countsize ) ) {
2011-03-04 20:59:21 +01:00
break ;
2011-03-18 22:38:11 +01:00
}
2011-03-04 20:59:21 +01:00
}
2013-01-27 16:48:53 +01:00
/* for each disk, recover empty files, symlinks and empty dirs */
2011-10-15 08:39:52 +02:00
for ( i = 0 ; i < diskmax ; + + i ) {
tommy_node * node ;
struct snapraid_disk * disk ;
if ( ! handle [ i ] . disk )
continue ;
2013-08-26 20:20:36 +02:00
/* for each empty file in the disk */
2011-10-15 08:39:52 +02:00
disk = handle [ i ] . disk ;
node = disk - > filelist ;
while ( node ) {
char path [ PATH_MAX ] ;
struct stat st ;
struct snapraid_file * file ;
int failed = 0 ;
file = node - > data ;
node = node - > next ; /* next node */
/* if not empty, it's already checked and continue to the next one */
if ( file - > size ! = 0 ) {
continue ;
}
/* if excluded continue to the next one */
if ( file_flag_has ( file , FILE_IS_EXCLUDED ) ) {
continue ;
}
/* stat the file */
pathprint ( path , sizeof ( path ) , " %s%s " , disk - > dir , file - > sub ) ;
ret = stat ( path , & st ) ;
if ( ret = = - 1 ) {
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " Error stating empty file '%s'. %s. \n " , path , strerror ( errno ) ) ;
fprintf ( stdlog , " error:%s:%s: Empty file stat error \n " , disk - > name , file - > sub ) ;
2011-10-15 08:39:52 +02:00
+ + error ;
} else if ( ! S_ISREG ( st . st_mode ) ) {
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " error:%s:%s: Empty file error for not regular file \n " , disk - > name , file - > sub ) ;
2011-10-15 08:39:52 +02:00
+ + error ;
} else if ( st . st_size ! = 0 ) {
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " error:%s:%s: Empty file error for size '% " PRIu64 " ' \n " , disk - > name , file - > sub , st . st_size ) ;
2011-10-15 08:39:52 +02:00
+ + error ;
}
if ( fix & & failed ) {
int f ;
/* create the ancestor directories */
2013-02-20 13:29:58 +01:00
ret = mkancestor ( path ) ;
2011-10-15 08:39:52 +02:00
if ( ret ! = 0 ) {
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
/* create it */
2012-11-04 00:26:30 +01:00
/* O_NOFOLLOW: do not follow links to ensure to open the real file */
2011-10-15 08:39:52 +02:00
f = open ( path , O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_NOFOLLOW , 0600 ) ;
if ( f = = - 1 ) {
fprintf ( stderr , " Error creating empty file '%s'. %s. \n " , path , strerror ( errno ) ) ;
if ( errno = = EACCES ) {
fprintf ( stderr , " WARNING! Please give write permission to the file. \n " ) ;
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
}
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
2013-08-26 20:20:36 +02:00
/* set the original modification time */
ret = file_utime ( file , f ) ;
if ( ret ! = 0 ) {
close ( f ) ;
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
2011-10-15 08:39:52 +02:00
/* close it */
2013-08-26 20:20:36 +02:00
ret = close ( f ) ;
if ( ret ! = 0 ) {
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
2011-10-15 08:39:52 +02:00
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " fixed:%s:%s: Fixed empty file \n " , disk - > name , file - > sub ) ;
2011-10-15 08:39:52 +02:00
+ + recovered_error ;
2013-03-01 20:13:56 +01:00
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " status:recovered:%s:%s \n " , disk - > name , file - > sub ) ;
2013-10-31 18:41:10 +01:00
if ( ! state - > opt . quiet ) {
2013-10-30 22:59:33 +01:00
printf ( " recovered %s%s \n " , disk - > dir , file - > sub ) ;
}
2011-10-15 08:39:52 +02:00
}
}
2011-08-26 14:18:38 +02:00
/* for each link in the disk */
2011-10-01 19:18:32 +02:00
disk = handle [ i ] . disk ;
2011-08-26 14:18:38 +02:00
node = disk - > linklist ;
while ( node ) {
char path [ PATH_MAX ] ;
2013-01-27 18:06:15 +01:00
char pathto [ PATH_MAX ] ;
2011-08-26 14:18:38 +02:00
char linkto [ PATH_MAX ] ;
2013-01-27 18:06:15 +01:00
struct stat st ;
struct stat stto ;
2011-08-26 14:18:38 +02:00
struct snapraid_link * link ;
int failed = 0 ;
2013-03-01 18:48:13 +01:00
int unrecoverable = 0 ;
2011-08-26 14:18:38 +02:00
link = node - > data ;
node = node - > next ; /* next node */
/* if excluded continue to the next one */
if ( link_flag_has ( link , FILE_IS_EXCLUDED ) ) {
continue ;
}
2013-02-28 21:56:02 +01:00
if ( link_flag_has ( link , FILE_IS_HARDLINK ) ) {
2013-01-27 18:06:15 +01:00
/* stat the link */
pathprint ( path , sizeof ( path ) , " %s%s " , disk - > dir , link - > sub ) ;
ret = stat ( path , & st ) ;
if ( ret = = - 1 ) {
failed = 1 ;
2011-08-26 14:18:38 +02:00
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " Error stating hardlink '%s'. %s. \n " , path , strerror ( errno ) ) ;
fprintf ( stdlog , " hardlinkerror:%s:%s:%s: Hardlink stat error \n " , disk - > name , link - > sub , link - > linkto ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
} else if ( ! S_ISREG ( st . st_mode ) ) {
failed = 1 ;
2011-08-26 14:18:38 +02:00
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " hardlinkerror:%s:%s:%s: Hardlink error for not regular file \n " , disk - > name , link - > sub , link - > linkto ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
}
/* stat the "to" file */
pathprint ( pathto , sizeof ( pathto ) , " %s%s " , disk - > dir , link - > linkto ) ;
ret = stat ( pathto , & stto ) ;
if ( ret = = - 1 ) {
failed = 1 ;
2013-03-01 18:48:13 +01:00
if ( errno = = ENOENT ) {
unrecoverable = 1 ;
2013-06-29 18:53:58 +02:00
if ( fix ) {
/* if the target doesn't exist, it's unrecoverable */
/* because we cannot create an hardlink of a file that */
/* doesn't exists */
+ + unrecoverable_error ;
} else {
/* but in check, we can assume that fixing will recover */
/* such missing file, so we assume a less drastic error */
+ + error ;
}
2013-03-01 18:48:13 +01:00
}
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " Error stating hardlink-to '%s'. %s. \n " , pathto , strerror ( errno ) ) ;
fprintf ( stdlog , " hardlinkerror:%s:%s:%s: Hardlink to stat error \n " , disk - > name , link - > sub , link - > linkto ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
2013-02-24 19:02:53 +01:00
} else if ( ! S_ISREG ( stto . st_mode ) ) {
2013-01-27 18:06:15 +01:00
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " hardlinkerror:%s:%s:%s: Hardlink-to error for not regular file \n " , disk - > name , link - > sub , link - > linkto ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
2013-02-24 19:02:53 +01:00
} else if ( ! failed & & st . st_ino ! = stto . st_ino ) {
2013-01-27 18:06:15 +01:00
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " Mismatch hardlink '%s' and '%s'. Different inode. \n " , path , pathto ) ;
fprintf ( stdlog , " hardlinkerror:%s:%s:%s: Hardlink mismatch for different inode \n " , disk - > name , link - > sub , link - > linkto ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
}
2011-08-26 14:18:38 +02:00
} else {
2013-01-27 18:06:15 +01:00
/* read the symlink */
pathprint ( path , sizeof ( path ) , " %s%s " , disk - > dir , link - > sub ) ;
ret = readlink ( path , linkto , sizeof ( linkto ) ) ;
if ( ret < 0 ) {
2011-08-26 14:18:38 +02:00
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " Error reading symlink '%s'. %s. \n " , path , strerror ( errno ) ) ;
fprintf ( stdlog , " symlinkerror:%s:%s: Symlink read error \n " , disk - > name , link - > sub ) ;
2011-08-26 14:18:38 +02:00
+ + error ;
2013-02-21 18:50:27 +01:00
} else if ( ret > = PATH_MAX ) {
2013-01-27 18:06:15 +01:00
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " Error reading symlink '%s'. Symlink too long. \n " , path ) ;
fprintf ( stdlog , " symlinkerror:%s:%s: Symlink read error \n " , disk - > name , link - > sub ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
} else {
linkto [ ret ] = 0 ;
if ( strcmp ( linkto , link - > linkto ) ! = 0 ) {
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " symlinkerror:%s:%s: Symlink data error '%s' instead of '%s' \n " , disk - > name , link - > sub , linkto , link - > linkto ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
}
2011-08-26 14:18:38 +02:00
}
}
2013-03-01 18:48:13 +01:00
if ( fix & & failed & & ! unrecoverable ) {
2011-08-26 14:18:38 +02:00
/* create the ancestor directories */
2013-02-20 13:29:58 +01:00
ret = mkancestor ( path ) ;
2011-08-26 14:18:38 +02:00
if ( ret ! = 0 ) {
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
/* if it exists, it must be deleted before recreating */
ret = remove ( path ) ;
if ( ret ! = 0 & & errno ! = ENOENT ) {
fprintf ( stderr , " Error removing '%s'. %s. \n " , path , strerror ( errno ) ) ;
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
/* create it */
2013-02-28 21:56:02 +01:00
if ( link_flag_has ( link , FILE_IS_HARDLINK ) ) {
2013-01-27 18:06:15 +01:00
ret = hardlink ( pathto , path ) ;
if ( ret ! = 0 ) {
2013-03-01 20:13:56 +01:00
fprintf ( stderr , " Error writing hardlink '%s' to '%s'. %s. \n " , path , pathto , strerror ( errno ) ) ;
2013-01-27 18:06:15 +01:00
if ( errno = = EACCES ) {
fprintf ( stderr , " WARNING! Please give write permission to the hardlink. \n " ) ;
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
}
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
2011-08-26 14:18:38 +02:00
}
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " hardlinkfixed:%s:%s: Fixed hardlink error \n " , disk - > name , link - > sub ) ;
2013-01-27 18:06:15 +01:00
+ + recovered_error ;
} else {
ret = symlink ( link - > linkto , path ) ;
if ( ret ! = 0 ) {
2013-03-01 20:13:56 +01:00
fprintf ( stderr , " Error writing symlink '%s' to '%s'. %s. \n " , path , link - > linkto , strerror ( errno ) ) ;
2013-01-27 18:06:15 +01:00
if ( errno = = EACCES ) {
fprintf ( stderr , " WARNING! Please give write permission to the symlink. \n " ) ;
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
}
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " symlinkfixed:%s:%s: Fixed symlink error \n " , disk - > name , link - > sub ) ;
2013-01-27 18:06:15 +01:00
+ + recovered_error ;
}
2013-03-01 20:13:56 +01:00
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " status:recovered:%s:%s \n " , disk - > name , link - > sub ) ;
2013-10-31 18:41:10 +01:00
if ( ! state - > opt . quiet ) {
2013-10-30 22:59:33 +01:00
printf ( " recovered %s%s \n " , disk - > dir , link - > sub ) ;
}
2013-01-27 16:44:04 +01:00
}
}
/* for each dir in the disk */
disk = handle [ i ] . disk ;
node = disk - > dirlist ;
while ( node ) {
char path [ PATH_MAX ] ;
struct stat st ;
struct snapraid_dir * dir ;
int failed = 0 ;
dir = node - > data ;
node = node - > next ; /* next node */
/* if excluded continue to the next one */
if ( dir_flag_has ( dir , FILE_IS_EXCLUDED ) ) {
continue ;
}
/* stat the dir */
pathprint ( path , sizeof ( path ) , " %s%s " , disk - > dir , dir - > sub ) ;
ret = stat ( path , & st ) ;
if ( ret = = - 1 ) {
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " Error stating dir '%s'. %s. \n " , path , strerror ( errno ) ) ;
fprintf ( stdlog , " dir_error:%s:%s: Dir stat error \n " , disk - > name , dir - > sub ) ;
2013-01-27 16:44:04 +01:00
+ + error ;
} else if ( ! S_ISDIR ( st . st_mode ) ) {
failed = 1 ;
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " dir_error:%s:%s: Dir error for not directory \n " , disk - > name , dir - > sub ) ;
2013-01-27 16:44:04 +01:00
+ + error ;
}
if ( fix & & failed ) {
/* create the ancestor directories */
2013-02-20 13:29:58 +01:00
ret = mkancestor ( path ) ;
2013-01-27 16:44:04 +01:00
if ( ret ! = 0 ) {
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
/* create it */
ret = mkdir ( path , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) ;
if ( ret ! = 0 ) {
fprintf ( stderr , " Error creating dir '%s'. %s. \n " , path , strerror ( errno ) ) ;
if ( errno = = EACCES ) {
fprintf ( stderr , " WARNING! Please give write permission to the dir. \n " ) ;
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
}
printf ( " Stopping \n " ) ;
+ + unrecoverable_error ;
goto bail ;
}
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " dir_fixed:%s:%s: Fixed dir error \n " , disk - > name , dir - > sub ) ;
2013-01-27 16:44:04 +01:00
+ + recovered_error ;
2013-03-01 20:13:56 +01:00
2013-10-30 21:45:53 +01:00
fprintf ( stdlog , " status:recovered:%s:%s \n " , disk - > name , dir - > sub ) ;
2013-10-31 18:41:10 +01:00
if ( ! state - > opt . quiet ) {
2013-10-30 22:59:33 +01:00
printf ( " recovered %s%s \n " , disk - > dir , dir - > sub ) ;
}
2011-08-26 14:18:38 +02:00
}
}
}
2011-06-20 20:35:04 +02:00
state_progress_end ( state , countpos , countmax , countsize ) ;
2011-03-04 20:59:21 +01:00
2011-03-18 22:38:11 +01:00
bail :
2013-05-31 23:34:59 +02:00
/* close all the files left open */
2011-03-18 22:38:11 +01:00
for ( j = 0 ; j < diskmax ; + + j ) {
2013-05-31 23:34:59 +02:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
fprintf ( stderr , " DANGER! Unexpected close error in a data disk. \n " ) ;
+ + unrecoverable_error ;
/* continue, as we are already exiting */
}
}
2013-04-28 19:01:36 +02:00
2013-06-01 08:31:47 +02:00
/* remove all the files created from scratch that have not finished the processing */
2013-05-31 23:34:59 +02:00
/* it happens only when aborting */
if ( fix ) {
/* for each disk */
for ( i = 0 ; i < diskmax ; + + i ) {
tommy_node * node ;
struct snapraid_disk * disk ;
2013-04-28 19:01:36 +02:00
2013-05-31 23:34:59 +02:00
if ( ! handle [ i ] . disk )
continue ;
/* for each file in the disk */
disk = handle [ i ] . disk ;
node = disk - > filelist ;
while ( node ) {
2013-04-28 19:01:36 +02:00
char path [ PATH_MAX ] ;
2013-05-31 23:34:59 +02:00
struct snapraid_file * file ;
file = node - > data ;
node = node - > next ; /* next node */
/* if the file was not created, meaning that it was already existing */
if ( ! file_flag_has ( file , FILE_IS_CREATED ) ) {
/* nothing to do */
continue ;
}
2013-04-28 19:01:36 +02:00
2013-05-31 23:34:59 +02:00
/* if processing was finished */
if ( file_flag_has ( file , FILE_IS_FINISHED ) ) {
/* nothing to do */
continue ;
}
/* if the file was originally missing, and processing not yet finished */
/* we have to throw it away to ensure that at the next run we will retry */
/* to fix it, in case we select to undelete missing files */
pathprint ( path , sizeof ( path ) , " %s%s " , disk - > dir , file - > sub ) ;
2013-04-28 19:01:36 +02:00
2013-06-01 08:31:47 +02:00
ret = remove ( path ) ;
2013-04-28 19:01:36 +02:00
if ( ret ! = 0 ) {
2013-06-01 08:31:47 +02:00
fprintf ( stderr , " Error removing '%s'. %s. \n " , path , strerror ( errno ) ) ;
2013-04-28 19:01:36 +02:00
fprintf ( stderr , " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
+ + unrecoverable_error ;
/* continue, as we are already exiting */
}
}
2011-03-18 22:38:11 +01:00
}
}
2011-03-17 16:17:59 +01:00
if ( error | | recovered_error | | unrecoverable_error ) {
2013-10-19 13:24:35 +02:00
printf ( " \n " ) ;
printf ( " %8u read/data errors \n " , error ) ;
2011-03-17 16:17:59 +01:00
if ( fix ) {
2013-10-19 13:24:35 +02:00
printf ( " %8u recovered errors \n " , recovered_error ) ;
2011-03-17 16:17:59 +01:00
}
2013-10-19 13:24:35 +02:00
if ( unrecoverable_error ) {
printf ( " %8u UNRECOVERABLE errors \n " , unrecoverable_error ) ;
printf ( " DANGER! There are unrecoverable errors! \n " ) ;
} else {
2012-10-07 23:18:10 +02:00
/* without checking, we don't know if they are really recoverable or not */
if ( check )
2013-10-19 13:24:35 +02:00
printf ( " %8u unrecoverable errors \n " , unrecoverable_error ) ;
if ( fix )
2013-10-27 21:05:18 +01:00
printf ( " Everything OK \n " ) ;
2013-10-19 13:24:35 +02:00
else
printf ( " WARNING! There are errors! \n " ) ;
2012-10-07 23:18:10 +02:00
}
2011-03-04 20:59:21 +01:00
} else {
2013-09-21 19:11:11 +02:00
printf ( " Everything OK \n " ) ;
2011-03-04 20:59:21 +01:00
}
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " summary:error:%u \n " , error ) ;
if ( fix )
fprintf ( stdlog , " summary:error_recovered:%u \n " , recovered_error ) ;
if ( check )
fprintf ( stdlog , " summary:error_unrecoverable:%u \n " , unrecoverable_error ) ;
if ( fix ) {
if ( error + recovered_error + unrecoverable_error = = 0 )
fprintf ( stdlog , " summary:exit:ok \n " ) ;
else if ( unrecoverable_error = = 0 )
fprintf ( stdlog , " summary:exit:recovered \n " ) ;
else
fprintf ( stdlog , " summary:exit:unrecoverable \n " ) ;
} else if ( check ) {
if ( error + unrecoverable_error = = 0 )
fprintf ( stdlog , " summary:exit:ok \n " ) ;
else if ( unrecoverable_error = = 0 )
fprintf ( stdlog , " summary:exit:recoverable \n " ) ;
else
fprintf ( stdlog , " summary:exit:unrecoverable \n " ) ;
} else { /* audit only */
if ( error = = 0 )
fprintf ( stdlog , " summary:exit:ok \n " ) ;
else
fprintf ( stdlog , " summary:exit:error \n " ) ;
2013-09-30 22:24:30 +02:00
}
2013-10-03 22:44:33 +02:00
fflush ( stdlog ) ;
2013-09-30 22:24:30 +02:00
2011-06-11 12:05:02 +02:00
free ( failed ) ;
2012-11-02 14:07:18 +01:00
free ( failed_map ) ;
2011-03-04 20:59:21 +01:00
free ( handle ) ;
2011-04-23 21:38:26 +02:00
free ( buffer_alloc ) ;
free ( buffer ) ;
/* fails if some error are present after the run */
if ( fix ) {
2013-06-29 18:11:36 +02:00
if ( state - > opt . expect_unrecoverable ) {
2011-04-23 21:38:26 +02:00
if ( unrecoverable_error = = 0 )
return - 1 ;
} else {
if ( unrecoverable_error ! = 0 )
return - 1 ;
}
} else {
2013-06-29 18:11:36 +02:00
if ( state - > opt . expect_unrecoverable ) {
2011-04-23 21:38:26 +02:00
if ( unrecoverable_error = = 0 )
return - 1 ;
2013-06-29 18:11:36 +02:00
} else if ( state - > opt . expect_recoverable ) {
2013-06-29 18:53:58 +02:00
if ( unrecoverable_error ! = 0 | | error = = 0 )
2011-04-23 21:38:26 +02:00
return - 1 ;
} else {
if ( error ! = 0 | | unrecoverable_error ! = 0 )
return - 1 ;
}
}
2011-03-04 20:59:21 +01:00
2011-03-18 22:38:11 +01:00
return 0 ;
2011-03-04 20:59:21 +01:00
}
2013-06-29 17:53:40 +02:00
int state_check ( struct snapraid_state * state , int check , int fix , block_off_t blockstart , block_off_t blockcount )
2011-03-04 20:59:21 +01:00
{
2011-03-12 10:09:59 +01:00
block_off_t blockmax ;
data_off_t size ;
2012-10-09 23:02:46 +02:00
data_off_t out_size ;
2011-03-04 20:59:21 +01:00
int ret ;
2013-09-15 21:12:41 +02:00
struct snapraid_parity parity [ LEV_MAX ] ;
struct snapraid_parity * parity_ptr [ LEV_MAX ] ;
2011-04-23 21:38:26 +02:00
unsigned error ;
2013-09-15 21:12:41 +02:00
unsigned l ;
2011-03-04 20:59:21 +01:00
2012-03-13 23:45:05 +01:00
printf ( " Initializing... \n " ) ;
2011-03-04 20:59:21 +01:00
2012-10-07 23:18:10 +02:00
if ( ! check & & fix ) {
fprintf ( stderr , " Error in calling, you cannot fix without checking parity. \n " ) ;
exit ( EXIT_FAILURE ) ;
}
2012-10-09 23:02:46 +02:00
blockmax = parity_size ( state ) ;
2011-03-12 10:09:59 +01:00
size = blockmax * ( data_off_t ) state - > block_size ;
2011-03-04 20:59:21 +01:00
2011-03-17 16:17:59 +01:00
if ( blockstart > blockmax ) {
2011-03-18 22:38:11 +01:00
fprintf ( stderr , " Error in the specified starting block %u. It's bigger than the parity size %u. \n " , blockstart , blockmax ) ;
2011-03-04 20:59:21 +01:00
exit ( EXIT_FAILURE ) ;
}
2011-03-23 18:54:08 +01:00
/* adjust the number of block to process */
if ( blockcount ! = 0 & & blockstart + blockcount < blockmax ) {
blockmax = blockstart + blockcount ;
}
2011-03-04 20:59:21 +01:00
if ( fix ) {
/* if fixing, create the file and open for writing */
2011-03-18 22:38:11 +01:00
/* if it fails, we cannot continue */
2013-09-15 21:12:41 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
parity_ptr [ l ] = & parity [ l ] ;
ret = parity_create ( parity_ptr [ l ] , state - > parity_path [ l ] , & out_size , state - > opt . skip_sequential ) ;
2012-10-09 23:02:46 +02:00
if ( ret = = - 1 ) {
2013-09-15 21:12:41 +02:00
fprintf ( stderr , " WARNING! Without an accessible %s file, it isn't possible to fix any error. \n " , lev_name ( l ) ) ;
2013-09-11 23:30:32 +02:00
exit ( EXIT_FAILURE ) ;
}
2013-09-15 21:12:41 +02:00
ret = parity_chsize ( parity_ptr [ l ] , size , & out_size , state - > opt . skip_fallocate ) ;
2013-09-11 23:30:32 +02:00
if ( ret = = - 1 ) {
2013-09-15 21:12:41 +02:00
fprintf ( stderr , " WARNING! Without an accessible %s file, it isn't possible to sync. \n " , lev_name ( l ) ) ;
2013-09-11 23:30:32 +02:00
exit ( EXIT_FAILURE ) ;
}
}
2012-10-07 23:18:10 +02:00
} else if ( check ) {
2011-03-04 20:59:21 +01:00
/* if checking, open the file for reading */
2011-03-18 22:38:11 +01:00
/* it may fail if the file doesn't exist, in this case we continue to check the files */
2013-09-15 21:12:41 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
parity_ptr [ l ] = & parity [ l ] ;
ret = parity_open ( parity_ptr [ l ] , state - > parity_path [ l ] , state - > opt . skip_sequential ) ;
2011-10-09 07:51:25 +02:00
if ( ret = = - 1 ) {
2013-09-15 21:12:41 +02:00
printf ( " No accessible %s file, only files will be checked. \n " , lev_name ( l ) ) ;
2011-04-23 21:38:26 +02:00
/* continue anyway */
2013-09-15 21:12:41 +02:00
parity_ptr [ l ] = 0 ;
2011-04-23 21:38:26 +02:00
}
2013-09-11 23:30:32 +02:00
}
2012-10-07 23:18:10 +02:00
} else {
/* otherwise don't use any parity */
2013-09-15 21:12:41 +02:00
for ( l = 0 ; l < state - > level ; + + l )
parity_ptr [ l ] = 0 ;
2011-03-04 20:59:21 +01:00
}
2012-02-02 19:26:33 +01:00
if ( fix )
printf ( " Fixing... \n " ) ;
2012-10-07 23:18:10 +02:00
else if ( check )
2012-02-02 19:26:33 +01:00
printf ( " Checking... \n " ) ;
2012-10-07 23:18:10 +02:00
else
printf ( " Hashing... \n " ) ;
2012-02-02 19:26:33 +01:00
2011-04-23 21:38:26 +02:00
error = 0 ;
2011-03-19 22:00:08 +01:00
2011-03-17 16:17:59 +01:00
/* skip degenerated cases of empty parity, or skipping all */
if ( blockstart < blockmax ) {
2013-09-15 21:12:41 +02:00
ret = state_check_process ( state , check , fix , parity_ptr , blockstart , blockmax ) ;
2011-03-17 16:17:59 +01:00
if ( ret = = - 1 ) {
2011-04-23 21:38:26 +02:00
+ + error ;
2011-03-19 22:00:08 +01:00
/* continue, as we are already exiting */
2011-03-17 16:17:59 +01:00
}
2011-03-04 20:59:21 +01:00
}
2011-03-18 22:38:11 +01:00
/* try to close only if opened */
2013-09-15 21:12:41 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
if ( parity_ptr [ l ] ) {
ret = parity_close ( parity_ptr [ l ] ) ;
2013-09-11 23:30:32 +02:00
if ( ret = = - 1 ) {
2013-09-15 21:12:41 +02:00
fprintf ( stderr , " DANGER! Unexpected close error in %s disk. \n " , lev_name ( l ) ) ;
2013-09-11 23:30:32 +02:00
+ + error ;
/* continue, as we are already exiting */
}
}
}
2011-04-23 21:38:26 +02:00
/* abort if error are present */
if ( error ! = 0 )
2013-06-29 17:53:40 +02:00
return - 1 ;
return 0 ;
2011-03-19 22:00:08 +01:00
}
2011-03-22 23:23:45 +01:00