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"
2015-01-02 18:52:04 +01:00
# include "support.h"
2011-03-04 20:59:21 +01:00
# include "util.h"
# include "elem.h"
2013-02-27 21:07:16 +01:00
# include "import.h"
2014-10-19 21:07:17 +02:00
# include "search.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
/**
2014-02-01 17:57:26 +01:00
* If we know for sure that the block is garbage or missing
* and it needs to be recovered and rewritten to the disk .
2013-02-24 11:10:10 +01:00
*/
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 .
*
2014-02-01 23:32:53 +01:00
* Note that this could happen only for CHG blocks .
2013-02-24 11:10:10 +01:00
*/
int is_outofdate ;
2012-02-09 23:17:36 +01:00
unsigned index ; /**< Index of the failed block. */
2015-10-03 10:43:02 +02:00
struct snapraid_block * block ; /**< The failed block */
struct snapraid_disk * disk ; /**< The failed disk. */
struct snapraid_file * file ; /**< The failed file. 0 for DELETED block. */
block_off_t file_pos ; /**< Offset inside the file */
struct snapraid_handle * handle ; /**< The handle 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
/**
2015-10-07 22:07:20 +02:00
* Check 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
*/
2015-10-03 10:43:02 +02:00
static int blockcmp ( struct snapraid_state * state , int rehash , struct snapraid_block * block , unsigned pos_size , unsigned char * buffer , unsigned char * buffer_zero )
2011-04-28 21:38:25 +02:00
{
2016-04-07 21:39:02 +02:00
unsigned char hash [ HASH_MAX ] ;
2011-04-28 21:38:25 +02:00
/* now compute the hash of the valid part */
2013-07-01 23:08:25 +02:00
if ( rehash ) {
2015-10-03 10:43:02 +02:00
memhash ( state - > prevhash , state - > prevhashseed , hash , buffer , pos_size ) ;
2013-07-01 23:08:25 +02:00
} else {
2015-10-03 10:43:02 +02:00
memhash ( state - > hash , state - > hashseed , hash , buffer , pos_size ) ;
2013-07-01 23:08:25 +02:00
}
2011-04-28 21:38:25 +02:00
/* compare the hash */
2016-04-07 21:39:02 +02:00
if ( memcmp ( hash , block - > hash , BLOCK_HASH_SIZE ) ! = 0 ) {
2011-04-28 21:38:25 +02:00
return - 1 ;
}
/* compare to the end of the block */
2015-10-03 10:43:02 +02:00
if ( pos_size < state - > block_size ) {
if ( memcmp ( buffer + pos_size , buffer_zero + pos_size , state - > block_size - pos_size ) ! = 0 ) {
2011-04-28 21:38:25 +02:00
return - 1 ;
}
}
return 0 ;
}
2013-09-18 21:56:06 +02:00
/**
2015-10-07 22:07:20 +02:00
* Check if the hash of all the failed block we are expecting to recover are now matching .
2013-09-18 21:56:06 +02:00
*/
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 */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2014-02-01 17:57:26 +01:00
/* if we are expected to recover this block */
if ( ! failed [ failed_map [ j ] ] . is_outofdate
2014-09-27 00:32:39 +02:00
/* if the block has a hash to check */
2014-02-01 17:57:26 +01:00
& & block_has_updated_hash ( failed [ failed_map [ j ] ] . block )
) {
2014-03-18 21:04:25 +01:00
/* if a hash doesn't match, fail the check */
2015-10-03 10:43:02 +02:00
unsigned pos_size = file_block_size ( failed [ failed_map [ j ] ] . file , failed [ failed_map [ j ] ] . file_pos , state - > block_size ) ;
if ( blockcmp ( state , rehash , failed [ failed_map [ j ] ] . block , pos_size , buffer [ failed [ failed_map [ j ] ] . index ] , buffer_zero ) ! = 0 ) {
2015-04-20 22:07:42 +02:00
log_tag ( " hash_error: Hash mismatch on entry %u \n " , failed_map [ j ] ) ;
2014-03-18 21:04:25 +01:00
return 0 ;
}
2013-09-18 21:56:06 +02:00
hash_checked = 1 ;
}
}
2014-03-18 21:04:25 +01:00
/* if nothing checked, we reject it */
2014-03-18 22:35:01 +01:00
/* note that we are excluding this case at upper level */
/* but checking again doesn't hurt */
if ( ! hash_checked ) {
/* LCOV_EXCL_START */
2014-03-18 21:04:25 +01:00
return 0 ;
2014-03-18 22:35:01 +01:00
/* LCOV_EXCL_STOP */
}
2013-09-18 21:56:06 +02:00
2014-03-18 21:04:25 +01:00
/* if we checked something, and no block failed the check */
/* recompute all the redundancy information */
raid_gen ( diskmax , state - > level , state - > block_size , buffer ) ;
return 1 ;
2013-09-18 21:56:06 +02:00
}
/**
2015-10-07 22:07:20 +02:00
* Check if specified parity is now matching with a recomputed one .
2013-09-18 21:56:06 +02:00
*/
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 */
2014-01-08 17:07:13 +01:00
raid_gen ( diskmax , i + 1 , state - > block_size , buffer ) ;
2013-09-18 21:56:06 +02:00
/* if the recovered parity block matches */
2014-09-27 00:32:39 +02:00
if ( memcmp ( buffer [ diskmax + i ] , buffer_recov [ i ] , state - > block_size ) = = 0 ) {
2013-09-18 21:56:06 +02:00
/* recompute all the redundancy information */
2014-01-08 17:07:13 +01:00
raid_gen ( 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 .
2015-10-07 22:07:20 +02:00
* Return < 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
2014-03-20 22:10:19 +01:00
/* no fix required, already checked at higher level, but just to be sure */
2011-04-23 21:38:26 +02:00
if ( failed_count = = 0 ) {
2014-03-20 22:10:19 +01:00
/* LCOV_EXCL_START */
2013-10-25 18:03:05 +02:00
/* recompute only the parity */
2014-01-08 17:07:13 +01:00
raid_gen ( diskmax , state - > level , state - > block_size , buffer ) ;
2011-04-23 21:38:26 +02:00
return 0 ;
2014-03-20 22:10:19 +01:00
/* LCOV_EXCL_STOP */
2011-04-23 21:38:26 +02:00
}
2014-03-18 21:04:25 +01:00
2013-10-29 22:22:40 +01:00
n = state - > level ;
2013-09-18 21:56:06 +02:00
error = 0 ;
2013-10-25 18:03:05 +02:00
/* setup vector of failed disk indexes */
2014-09-27 00:32:39 +02:00
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 ;
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < failed_count ; + + i ) {
2014-02-01 17:57:26 +01:00
/* if we are expected to recover this block */
if ( ! failed [ failed_map [ i ] ] . is_outofdate
2016-04-10 22:44:40 +02:00
/* if the block has a hash to check */
2014-02-01 17:57:26 +01:00
& & block_has_updated_hash ( failed [ failed_map [ i ] ] . block )
)
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 */
2014-09-27 00:32:39 +02:00
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 */
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < r - 1 ; + + i )
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 */
2015-03-21 17:16:16 +01:00
raid_data ( 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 */
2014-09-27 00:32:39 +02:00
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 */
2015-04-20 22:07:42 +02:00
log_tag ( " parity_error:%u: " , pos ) ;
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < r ; + + i ) {
2013-10-29 22:22:40 +01:00
if ( i ! = 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " / " ) ;
log_tag ( " %s " , lev_config_name ( ip [ i ] ) ) ;
2013-10-25 18:03:05 +02:00
}
2015-04-20 22:07:42 +02:00
log_tag ( " :parity: Parity mismatch \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 */
2014-09-27 00:32:39 +02:00
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 */
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < r ; + + i )
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 */
2015-03-21 17:16:16 +01:00
raid_data ( 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 */
2015-04-20 22:07:42 +02:00
log_tag ( " parity_error:%u: " , pos ) ;
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < r ; + + i ) {
2013-10-29 22:22:40 +01:00
if ( i ! = 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " / " ) ;
log_tag ( " %s " , lev_config_name ( ip [ i ] ) ) ;
2013-10-25 18:03:05 +02:00
}
2015-04-20 22:07:42 +02:00
log_tag ( " :hash: Hash mismatch \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 ;
2014-03-02 17:05:21 +01:00
2015-04-20 22:07:42 +02:00
log_tag ( " strategy_error:%u: No strategy to recover from %u failures with %u parity %s hash \n " ,
2014-03-02 17:05:21 +01:00
pos , failed_count , n , has_hash ? " with " : " without " ) ;
return - 1 ;
2012-02-09 23:17:36 +01:00
}
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 ;
2014-03-04 19:34:55 +01:00
int something_unsynced ;
2016-02-09 23:12:33 +01:00
char esc_buffer [ ESC_MAX ] ;
2012-02-09 23:17:36 +01:00
error = 0 ;
2014-02-01 17:57:26 +01:00
/* if nothing failed, just recompute the parity */
if ( failed_count = = 0 ) {
raid_gen ( diskmax , state - > level , state - > block_size , buffer ) ;
return 0 ;
}
2014-03-18 21:04:25 +01:00
/* logs the status */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2015-08-05 09:42:11 +02:00
const char * desc ;
2014-03-18 21:04:25 +01:00
const char * hash ;
const char * data ;
struct snapraid_block * block = failed [ j ] . block ;
unsigned block_state = block_state_get ( block ) ;
switch ( block_state ) {
2015-08-05 09:42:11 +02:00
case BLOCK_STATE_DELETED : desc = " delete " ; break ;
case BLOCK_STATE_CHG : desc = " change " ; break ;
case BLOCK_STATE_REP : desc = " replace " ; break ;
case BLOCK_STATE_BLK : desc = " block " ; break ;
2014-03-18 22:35:01 +01:00
/* LCOV_EXCL_START */
2015-08-05 09:42:11 +02:00
default : desc = " unknown " ; break ;
2014-09-27 00:32:39 +02:00
/* LCOV_EXCL_STOP */
2014-03-18 21:04:25 +01:00
}
if ( hash_is_invalid ( block - > hash ) ) {
hash = " lost " ;
} else if ( hash_is_zero ( block - > hash ) ) {
hash = " zero " ;
} else {
hash = " known " ;
}
if ( failed [ j ] . is_bad )
data = " bad " ;
else
data = " good " ;
2015-10-03 10:43:02 +02:00
if ( failed [ j ] . file ) {
struct snapraid_disk * disk = failed [ j ] . disk ;
struct snapraid_file * file = failed [ j ] . file ;
block_off_t file_pos = failed [ j ] . file_pos ;
2014-03-18 21:04:25 +01:00
2016-05-29 18:19:13 +02:00
log_tag ( " entry:%u:%s:%s:%s:%s:%s:%u: \n " , j , desc , hash , data , disk - > name , esc_tag ( file - > sub , esc_buffer ) , file_pos ) ;
2014-03-18 21:04:25 +01:00
} else {
2015-08-05 09:42:11 +02:00
log_tag ( " entry:%u:%s:%s:%s: \n " , j , desc , hash , data ) ;
2014-03-18 21:04:25 +01:00
}
}
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
2014-02-01 17:57:26 +01:00
/* Note that if the 'sync' ends normally, we don't have any DELETED, REP and CHG blocks */
/* and the two strategies are identical */
/* As first, we assume that the parity IS updated 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'. */
2014-02-01 17:57:26 +01:00
/* In this case, parity contains info from BLK, REP and CHG blocks, */
/* but not for DELETED. */
2013-02-27 21:07:16 +01:00
/* We need to put in the recovering process only the bad blocks, because all the */
2014-02-01 17:57:26 +01:00
/* others already contains the correct data read from disk, and the parity is correctly computed for them. */
/* We are interested to recover BLK, REP and CHG blocks if they are marked as bad, */
2013-02-27 21:07:16 +01:00
/* 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 */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2012-02-09 23:17:36 +01:00
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 */
2014-02-01 17:57:26 +01:00
/* if we have the hash for it */
if ( ( block_state = = BLOCK_STATE_BLK | | block_state = = BLOCK_STATE_REP )
2014-09-27 00:32:39 +02:00
/* try to fetch the block using the known hash */
2014-10-19 21:07:17 +02:00
& & ( state_import_fetch ( state , rehash , failed [ j ] . block , buffer [ failed [ j ] . index ] ) = = 0
2015-10-03 10:43:02 +02:00
| | state_search_fetch ( state , rehash , failed [ j ] . file , failed [ j ] . file_pos , failed [ j ] . block , buffer [ failed [ j ] . index ] ) = = 0 )
2014-02-01 17:57:26 +01:00
) {
/* we already have corrected it! */
2015-04-20 22:07:42 +02:00
log_tag ( " hash_import: Fixed entry %u \n " , j ) ;
2013-02-27 21:07:16 +01:00
} else {
2014-02-01 17:57:26 +01:00
/* otherwise try to recover it */
2013-02-27 21:07:16 +01:00
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 ) {
2015-04-20 22:07:42 +02:00
log_tag ( " recover_sync:%u:%u: Skipped for already recovered \n " , pos , n ) ;
2014-03-18 21:04:25 +01:00
2013-10-25 18:03:05 +02:00
/* recompute only the parity */
2014-01-08 17:07:13 +01:00
raid_gen ( 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 ) {
2014-02-01 17:57:26 +01:00
/* reprocess the CHG blocks, for which we don't have a hash to check */
2016-01-02 16:52:20 +01:00
/* if they were BAD we have to use some heuristics to ensure that we have recovered */
2012-06-20 23:05:15 +02:00
/* the state after the sync. If unsure, we assume the worst case */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2012-02-09 23:17:36 +01:00
/* 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
2014-02-01 17:57:26 +01:00
/* BLK and REP blocks are always OK, because at this point */
/* we have already checked their hash */
if ( block_state ! = BLOCK_STATE_CHG ) {
assert ( block_state = = BLOCK_STATE_BLK | | block_state = = BLOCK_STATE_REP ) ;
continue ;
}
/* for CHG blocks we have to 'guess' if they are correct or not */
/* if the hash is invalid we cannot check the result */
/* this could happen if we have lost this information */
/* after an aborted sync */
if ( hash_is_invalid ( failed [ j ] . block - > hash ) ) {
/* it may contain garbage */
failed [ j ] . is_outofdate = 1 ;
2014-03-18 21:04:25 +01:00
2015-04-20 22:07:42 +02:00
log_tag ( " hash_unknown: Unknown hash on entry %u \n " , j ) ;
2014-02-01 17:57:26 +01:00
} else if ( hash_is_zero ( failed [ j ] . block - > hash ) ) {
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 ;
2014-03-18 21:04:25 +01:00
2015-04-20 22:07:42 +02:00
log_tag ( " hash_unknown: Maybe old zero on entry %u \n " , j ) ;
2012-02-09 23:17:36 +01:00
}
2014-02-01 17:57:26 +01:00
} 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'. */
2015-10-03 10:43:02 +02:00
unsigned pos_size = file_block_size ( failed [ j ] . file , failed [ j ] . file_pos , state - > block_size ) ;
if ( blockcmp ( state , rehash , failed [ j ] . block , pos_size , buffer [ failed [ j ] . index ] , buffer_zero ) = = 0 ) {
2013-02-24 19:02:53 +01:00
/* it may contain garbage */
failed [ j ] . is_outofdate = 1 ;
2014-03-18 21:04:25 +01:00
2015-04-20 22:07:42 +02:00
log_tag ( " hash_unknown: Maybe old data on entry %u \n " , j ) ;
2013-02-24 19:02:53 +01:00
}
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 ;
2014-03-18 21:04:25 +01:00
if ( ret < 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " recover_sync:%u:%u: Failed with no attempts \n " , pos , n ) ;
2014-03-18 21:04:25 +01:00
else
2015-04-20 22:07:42 +02:00
log_tag ( " recover_sync:%u:%u: Failed with %d attempts \n " , pos , n , ret ) ;
2014-03-18 21:04:25 +01:00
2014-02-01 17:57:26 +01:00
/* Now assume that the parity IS NOT updated at the current state, */
2012-02-09 23:17:36 +01:00
/* but still represent the state before the last 'sync' process. */
2014-02-01 17:57:26 +01:00
/* In this case, parity contains info from BLK, REP (old version), CHG (old version) and DELETED blocks, */
/* but not for REP (new version) and CHG (new version). */
2013-02-27 21:07:16 +01:00
/* We are interested to recover BLK ones marked as bad, */
2014-02-01 17:57:26 +01:00
/* but we are not interested to recover CHG (new version) and REP (new version) blocks, */
/* even if marked as bad, because we don't have parity for them and it's just impossible, */
2013-02-27 21:07:16 +01:00
/* 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 */
2014-03-04 19:34:55 +01:00
something_unsynced = 0 ; /* keep track if we have some unsynced info to process */
2014-09-27 00:32:39 +02: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
2014-02-01 17:57:26 +01:00
| | block_state = = BLOCK_STATE_REP
2012-02-09 23:17:36 +01:00
) {
2014-02-01 17:57:26 +01:00
/* If the block is CHG, REP or DELETED, we don't have the original content of block, */
2013-02-27 21:07:16 +01:00
/* and we must try to recover it. */
2014-02-01 17:57:26 +01:00
/* This apply to CHG and REP 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, */
2014-02-01 17:57:26 +01:00
/* we are not really interested in DELETED, CHG (old version) and REP (old version). */
2014-03-04 19:34:55 +01:00
something_unsynced = 1 ;
2014-02-01 17:57:26 +01:00
if ( block_state = = BLOCK_STATE_CHG
& & hash_is_zero ( failed [ j ] . block - > hash )
) {
/* If the block was a ZERO block, restore it to the original 0 as before the 'sync' */
/* We do this to just allow recovering of other BLK ones */
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 */
/* try to fetch the old block using the old hash for CHG and DELETED blocks */
} else if ( ( block_state = = BLOCK_STATE_CHG | | block_state = = BLOCK_STATE_DELETED )
2016-04-10 19:13:08 +02:00
& & hash_is_unique ( failed [ j ] . block - > hash )
2014-10-19 21:07:17 +02:00
& & state_import_fetch ( state , rehash , failed [ j ] . block , 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. */
}
2014-03-18 21:04:25 +01:00
/* avoid to use the hash of this block to verify the recovering */
/* this applies to REP blocks because we are going to recover the old state */
/* and the REP hash represent the new one */
/* it also applies to CHG and DELETE blocks because we want to have */
2016-01-02 16:52:20 +01:00
/* a successful recovering only if a BLK one is matching */
2013-02-27 21:07:16 +01:00
failed [ j ] . is_outofdate = 1 ;
} 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 ;
2014-02-01 17:57:26 +01:00
2013-02-27 21:07:16 +01:00
/* 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 */
2014-03-04 19:34:55 +01:00
/* if nothing unsynced we also don't retry, because it's the same try as before */
if ( something_to_recover & & something_unsynced ) {
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 ) {
2014-03-18 21:04:25 +01:00
/* reprocess the REP and CHG blocks, for which we have recovered and old state */
/* that we don't want to save into disk */
/* we have already marked them, but we redo it for logging */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2014-03-18 21:04:25 +01:00
/* 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 ) ;
if ( block_state = = BLOCK_STATE_CHG
| | block_state = = BLOCK_STATE_REP
) {
/* mark that we have restored an old state */
/* and we don't want to write it to the disk */
failed [ j ] . is_outofdate = 1 ;
2015-04-20 22:07:42 +02:00
log_tag ( " hash_unknown: Surely old data on entry %u \n " , j ) ;
2014-03-18 21:04:25 +01:00
}
}
}
2013-02-27 21:07:16 +01:00
return 0 ;
2012-02-09 23:17:36 +01:00
}
2013-02-27 21:07:16 +01:00
if ( ret > 0 )
error + = ret ;
2014-03-18 21:04:25 +01:00
if ( ret < 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " recover_unsync:%u:%u: Failed with no attempts \n " , pos , n ) ;
2014-03-18 21:04:25 +01:00
else
2015-04-20 22:07:42 +02:00
log_tag ( " recover_unsync:%u:%u: Failed with %d attempts \n " , pos , n , ret ) ;
2014-03-18 21:04:25 +01:00
} else {
2015-04-20 22:07:42 +02:00
log_tag ( " recover_unsync:%u:%u: Skipped for%s%s \n " , pos , n ,
2014-03-18 21:04:25 +01:00
! something_to_recover ? " nothing to recover " : " " ,
2020-05-02 14:21:40 +02:00
! something_unsynced ? " nothing unsynced " : " "
2014-03-18 21:04:25 +01:00
) ;
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
}
2014-12-29 08:50:20 +01:00
/**
* Post process all the files at the specified block index : : i .
* For each file , if we are at the last block , closes it ,
2015-10-07 22:07:20 +02:00
* adjust the timestamp , and print the result .
2015-08-31 22:01:34 +02:00
*
2021-05-22 21:02:51 +02:00
* This works only if the whole file is processed , including its last block .
2021-05-22 22:17:42 +02:00
* This doesn ' t always happen , like with an explicit end block .
2021-05-22 21:02:51 +02:00
*
* In such case , the check / fix command won ' t report any information of the
2021-05-22 22:17:42 +02:00
* files partially checked .
2014-12-29 08:50:20 +01:00
*/
static int file_post ( struct snapraid_state * state , int fix , unsigned i , struct snapraid_handle * handle , unsigned diskmax )
{
unsigned j ;
int ret ;
2016-02-09 23:12:33 +01:00
char esc_buffer [ ESC_MAX ] ;
char esc_buffer_alt [ ESC_MAX ] ;
2014-12-29 08:50:20 +01:00
2015-10-07 22:07:20 +02:00
/* for all the files print the final status, and does the final time fix */
2014-12-29 08:50:20 +01:00
/* we also ensure to close files after processing the last block */
for ( j = 0 ; j < diskmax ; + + j ) {
struct snapraid_block * block ;
struct snapraid_disk * disk ;
struct snapraid_file * collide_file ;
struct snapraid_file * file ;
2015-10-03 10:43:02 +02:00
block_off_t file_pos ;
2014-12-29 08:50:20 +01:00
uint64_t inode ;
disk = handle [ j ] . disk ;
if ( ! disk ) {
/* if no disk, nothing to do */
continue ;
}
2016-01-16 14:26:05 +01:00
block = fs_par2block_find ( disk , i ) ;
2014-12-29 08:50:20 +01:00
if ( ! block_has_file ( block ) ) {
/* if no file, nothing to do */
continue ;
}
2015-10-03 10:43:02 +02:00
file = fs_par2file_get ( disk , i , & file_pos ) ;
2014-12-29 08:50:20 +01:00
/* if it isn't the last block in the file */
2015-10-03 10:43:02 +02:00
if ( ! file_block_is_last ( file , file_pos ) ) {
2014-12-29 08:50:20 +01:00
/* nothing to do */
continue ;
}
/* if the file is excluded, we have nothing to adjust as the file is never written */
if ( file_flag_has ( file , FILE_IS_EXCLUDED )
| | ( state - > opt . syncedonly & & file_flag_has ( file , FILE_IS_UNSYNCED ) ) ) {
/* nothing to do, but close the file */
goto close_and_continue ;
}
/* finish the fix process if it's the last block of the files */
if ( fix ) {
/* mark that we finished with this file */
/* to identify later any NOT finished ones */
file_flag_set ( file , FILE_IS_FINISHED ) ;
/* if the file is damaged, meaning that a fix failed */
if ( file_flag_has ( file , FILE_IS_DAMAGED ) ) {
/* rename it to .unrecoverable */
2021-05-22 09:05:21 +02:00
char path [ PATH_MAX ] ;
2014-12-29 08:50:20 +01:00
char path_to [ PATH_MAX ] ;
2021-05-22 09:05:21 +02:00
pathprint ( path , sizeof ( path ) , " %s%s " , disk - > dir , file - > sub ) ;
2014-12-29 08:50:20 +01:00
pathprint ( path_to , sizeof ( path_to ) , " %s%s.unrecoverable " , disk - > dir , file - > sub ) ;
/* ensure to close the file before renaming */
2015-09-02 19:37:24 +02:00
if ( handle [ j ] . file = = file ) {
ret = handle_close ( & handle [ j ] ) ;
if ( ret ! = 0 ) {
/* LCOV_EXCL_START */
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , i , disk - > name , esc_tag ( file - > sub , esc_buffer ) , strerror ( errno ) ) ;
2015-09-02 19:37:24 +02:00
log_fatal ( " DANGER! Unexpected close error in a data disk. \n " ) ;
return - 1 ;
/* LCOV_EXCL_STOP */
}
2014-12-29 08:50:20 +01:00
}
ret = rename ( path , path_to ) ;
if ( ret ! = 0 ) {
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " Error renaming '%s' to '%s'. %s. \n " , path , path_to , strerror ( errno ) ) ;
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2014-12-29 08:50:20 +01:00
return - 1 ;
/* LCOV_EXCL_STOP */
}
2016-05-29 18:19:13 +02:00
log_tag ( " status:unrecoverable:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " unrecoverable %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) ) ;
2014-12-29 08:50:20 +01:00
/* and do not set the time if damaged */
goto close_and_continue ;
}
/* if the file is not fixed, meaning that it is untouched */
if ( ! file_flag_has ( file , FILE_IS_FIXED ) ) {
/* nothing to do, but close the file */
goto close_and_continue ;
}
2015-09-02 19:37:24 +02:00
/* if the file is closed or different than the one expected, reopen it */
/* a different open file could happen when filtering for bad blocks */
if ( handle [ j ] . file ! = file ) {
/* close a potential different file */
ret = handle_close ( & handle [ j ] ) ;
if ( ret ! = 0 ) {
/* LCOV_EXCL_START */
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , i , disk - > name , esc_tag ( handle [ j ] . file - > sub , esc_buffer ) , strerror ( errno ) ) ;
2015-09-02 19:37:24 +02:00
log_fatal ( " DANGER! Unexpected close error in a data disk. \n " ) ;
return - 1 ;
/* LCOV_EXCL_STOP */
}
/* reopen it as readonly, as to set the mtime readonly access it's enough */
/* we know that the file exists because it has the FILE_IS_FIXED tag */
ret = handle_open ( & handle [ j ] , file , state - > file_mode , log_error , 0 ) ;
if ( ret ! = 0 ) {
/* LCOV_EXCL_START */
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Open error. %s \n " , i , disk - > name , esc_tag ( file - > sub , esc_buffer ) , strerror ( errno ) ) ;
2015-09-02 19:37:24 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
return - 1 ;
/* LCOV_EXCL_STOP */
}
}
2016-05-29 18:19:13 +02:00
log_tag ( " status:recovered:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " recovered %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) ) ;
2014-12-29 08:50:20 +01:00
inode = handle [ j ] . st . st_ino ;
/* search for the corresponding inode */
collide_file = tommy_hashdyn_search ( & disk - > inodeset , file_inode_compare_to_arg , & inode , file_inode_hash ( inode ) ) ;
/* if the inode is already in the database and it refers at a different file name, */
/* we can fix the file time ONLY if the time and size allow to differentiate */
/* 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 */
if ( ! collide_file /* if not in the database, there is no collision */
| | 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 */
| | collide_file - > mtime_nsec ! = file - > mtime_nsec /* same for mtime_nsec */
) {
/* set the original modification time */
ret = handle_utime ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
/* mark the file as damaged */
file_flag_set ( file , FILE_IS_DAMAGED ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2014-12-29 08:50:20 +01:00
return - 1 ;
/* LCOV_EXCL_STOP */
}
} else {
2016-05-29 18:19:13 +02:00
log_tag ( " collision:%s:%s:%s: Not setting modification time to avoid inode collision \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) , esc_tag ( collide_file - > sub , esc_buffer_alt ) ) ;
2014-12-29 08:50:20 +01:00
}
} else {
/* we are not fixing, but only checking */
/* print just the final status */
if ( file_flag_has ( file , FILE_IS_DAMAGED ) ) {
if ( state - > opt . auditonly ) {
2016-05-29 18:19:13 +02:00
log_tag ( " status:damaged:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " damaged %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) ) ;
2014-12-29 08:50:20 +01:00
} else {
2016-05-29 18:19:13 +02:00
log_tag ( " status:unrecoverable:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " unrecoverable %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) ) ;
2014-12-29 08:50:20 +01:00
}
} else if ( file_flag_has ( file , FILE_IS_FIXED ) ) {
2016-05-29 18:19:13 +02:00
log_tag ( " status:recoverable:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " recoverable %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) ) ;
2014-12-29 08:50:20 +01:00
} else {
2015-02-22 14:31:03 +01:00
/* we don't use msg_verbose() because it also goes into the log */
2016-01-05 10:42:46 +01:00
if ( msg_level > = MSG_VERBOSE ) {
2016-05-29 18:19:13 +02:00
log_tag ( " status:correct:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " correct %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) ) ;
2016-01-05 10:42:46 +01:00
}
2014-12-29 08:50:20 +01:00
}
}
close_and_continue :
/* if the opened file is the correct one, close it */
/* in case of excluded and fragmented files it's possible */
/* that the opened file is not the current one */
if ( handle [ j ] . file = = file ) {
/* ensure to close the file just after finishing with it */
2015-09-02 19:29:35 +02:00
/* to avoid to keep it open without any possible use */
2014-12-29 08:50:20 +01:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret ! = 0 ) {
/* LCOV_EXCL_START */
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , i , disk - > name , esc_tag ( file - > sub , esc_buffer ) , strerror ( errno ) ) ;
2015-09-02 19:29:35 +02:00
log_fatal ( " DANGER! Unexpected close error in a data disk. \n " ) ;
2014-12-29 08:50:20 +01:00
return - 1 ;
/* LCOV_EXCL_STOP */
}
}
}
return 0 ;
}
/**
* Check if we have to process the specified block index : : i .
*/
2014-12-29 19:11:03 +01:00
static int block_is_enabled ( struct snapraid_state * state , block_off_t i , struct snapraid_handle * handle , unsigned diskmax )
2014-12-29 08:50:20 +01:00
{
unsigned j ;
2015-10-17 17:36:40 +02:00
unsigned l ;
2014-12-29 08:50:20 +01:00
2021-07-08 07:38:24 +02:00
/* filter for bad blocks */
if ( state - > opt . badblockonly ) {
snapraid_info info ;
/* get block specific info */
info = info_get ( & state - > infoarr , i ) ;
/*
* Filter specifically only for bad blocks
*/
return info_get_bad ( info ) ;
}
2021-05-22 22:17:42 +02:00
/* filter for the parity */
2021-07-08 07:38:24 +02:00
if ( state - > opt . badfileonly ) {
2021-05-22 09:34:40 +02:00
snapraid_info info ;
/* get block specific info */
info = info_get ( & state - > infoarr , i ) ;
2021-05-22 22:17:42 +02:00
/*
* If the block is bad , it has to be processed
*
* This is not necessary in normal cases because if a block is bad ,
* it necessary needs to have a file related to it , and files with
* bad blocks are fully included .
*
* But some files may be excluded by additional filter options ,
* so it ' s not always true , and this ensures to always check all
* the bad blocks .
*/
if ( info_get_bad ( info ) )
2015-10-17 17:36:40 +02:00
return 1 ;
2021-05-22 22:17:42 +02:00
} else {
/* if a parity is not excluded, include all blocks, even unused ones */
for ( l = 0 ; l < state - > level ; + + l ) {
if ( ! state - > parity [ l ] . is_excluded_by_filter ) {
return 1 ;
}
2015-10-17 17:36:40 +02:00
}
}
2021-05-22 22:17:42 +02:00
/* filter for the files */
2014-12-29 08:50:20 +01:00
for ( j = 0 ; j < diskmax ; + + j ) {
struct snapraid_block * block ;
/* if no disk, nothing to check */
if ( ! handle [ j ] . disk )
continue ;
2016-01-16 14:26:05 +01:00
block = fs_par2block_find ( handle [ j ] . disk , i ) ;
2014-12-29 08:50:20 +01:00
/* try to recover all files, even the ones without hash */
/* because in some cases we can recover also them */
2015-10-03 10:43:02 +02:00
if ( block_has_file ( block ) ) {
struct snapraid_file * file = fs_par2file_get ( handle [ j ] . disk , i , 0 ) ;
if ( ! file_flag_has ( file , FILE_IS_EXCLUDED ) ) { /* only if the file is not filtered out */
2015-10-17 17:36:40 +02:00
return 1 ;
2015-10-03 10:43:02 +02:00
}
2014-12-29 08:50:20 +01:00
}
}
2015-10-17 17:36:40 +02:00
return 0 ;
2014-12-29 08:50:20 +01:00
}
2014-11-09 10:25:39 +01:00
static int state_check_process ( struct snapraid_state * state , int fix , struct snapraid_parity_handle * * 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 ;
2016-02-09 23:12:33 +01:00
char esc_buffer [ ESC_MAX ] ;
char esc_buffer_alt [ ESC_MAX ] ;
2021-05-22 11:37:36 +02:00
bit_vect_t * block_enabled ;
2025-03-27 12:57:41 -07:00
struct snapraid_bw bw ;
2011-03-04 20:59:21 +01:00
2016-01-18 22:38:01 +01:00
handle = handle_mapping ( state , & diskmax ) ;
2011-09-23 21:48:50 +02:00
2025-03-27 12:57:41 -07:00
/* initialize the bandwith context */
bw_init ( & bw , state - > opt . bwlimit ) ;
/* share the bandwidth context with all handles */
for ( j = 0 ; j < diskmax ; + + j )
handle [ j ] . bw = & bw ;
for ( j = 0 ; j < state - > level ; + + j )
if ( parity [ j ] )
parity [ j ] - > bw = & bw ;
2014-12-29 00:01:35 +01:00
/* we need 1 * data + 2 * parity + 1 * zero */
2014-01-14 22:10:47 +01:00
buffermax = diskmax + 2 * state - > level + 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 )
2014-01-02 17:51:50 +01:00
mtest_vector ( buffermax , state - > block_size , buffer ) ;
2013-09-05 22:29:58 +02:00
/* fill up the zero buffer */
2014-09-27 00:32:39 +02:00
memset ( buffer [ buffermax - 1 ] , 0 , state - > block_size ) ;
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 ;
2021-05-22 08:46:53 +02:00
msg_progress ( " Selecting... \n " ) ;
2011-03-30 00:14:43 +02:00
/* first count the number of blocks to process */
countmax = 0 ;
2021-05-22 11:37:36 +02:00
block_enabled = calloc_nofail ( 1 , bit_vect_size ( blockmax ) ) ; /* preinitialize to 0 */
2014-09-27 00:32:39 +02:00
for ( i = blockstart ; i < blockmax ; + + i ) {
2014-12-29 08:50:20 +01:00
if ( ! block_is_enabled ( state , i , handle , diskmax ) )
2011-03-30 00:14:43 +02:00
continue ;
2021-05-22 11:37:36 +02:00
bit_vect_set ( block_enabled , i ) ;
2011-03-30 00:14:43 +02:00
+ + countmax ;
}
2021-05-22 08:46:53 +02:00
if ( fix )
msg_progress ( " Fixing... \n " ) ;
else if ( ! state - > opt . auditonly )
msg_progress ( " Checking... \n " ) ;
else
msg_progress ( " Hashing... \n " ) ;
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 ;
2025-09-24 11:32:10 +02:00
if ( ! state_progress_begin ( state , blockstart , blockmax , countmax ) )
goto end ;
2014-09-27 00:32:39 +02:00
for ( i = blockstart ; i < blockmax ; + + i ) {
2011-04-23 21:38:26 +02:00
unsigned failed_count ;
2013-02-24 19:43:53 +01:00
int valid_parity ;
2017-03-26 18:10:46 +02:00
int used_parity ;
2013-07-01 23:08:25 +02:00
snapraid_info info ;
int rehash ;
2011-03-04 20:59:21 +01:00
2021-05-22 11:37:36 +02:00
if ( ! bit_vect_test ( block_enabled , i ) ) {
2021-05-22 21:02:51 +02:00
/* continue with the next block */
2011-03-04 20:59:21 +01:00
continue ;
2014-12-29 08:50:20 +01:00
}
2011-03-04 20:59:21 +01:00
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 */
2014-12-29 08:50:20 +01:00
/* but we don't report parity errors */
/* Note that with auditonly, we anyway skip the full parity check, */
2012-10-07 23:18:10 +02:00
/* 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
2017-03-26 18:10:46 +02:00
/* If the parity is used by at least one file */
used_parity = 0 ;
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 */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
2011-03-04 20:59:21 +01:00
int read_size ;
2016-04-07 21:39:02 +02:00
unsigned char hash [ HASH_MAX ] ;
2014-10-14 21:37:28 +02:00
struct snapraid_disk * disk ;
2011-03-04 20:59:21 +01:00
struct snapraid_block * block ;
2014-10-14 21:37:28 +02:00
struct snapraid_file * file ;
2015-10-03 10:43:02 +02:00
block_off_t file_pos ;
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 */
2014-10-14 21:37:28 +02:00
disk = handle [ j ] . disk ;
if ( ! disk ) {
2011-10-01 19:18:32 +02:00
/* use an empty block */
memset ( buffer [ j ] , 0 , state - > block_size ) ;
continue ;
}
/* if the disk block is not used */
2016-01-16 14:26:05 +01:00
block = fs_par2block_find ( disk , i ) ;
if ( block = = BLOCK_NULL ) {
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 ;
2014-02-01 17:57:26 +01:00
/* follow */
2013-02-24 19:43:53 +01:00
}
2014-01-14 22:10:47 +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 */
2015-10-03 10:43:02 +02:00
failed [ failed_count ] . is_bad = 0 ; /* note that is_bad==0 <=> file==0 */
2012-02-09 23:17:36 +01:00
failed [ failed_count ] . is_outofdate = 0 ;
failed [ failed_count ] . index = j ;
2013-02-24 19:02:53 +01:00
failed [ failed_count ] . block = block ;
2015-10-03 10:43:02 +02:00
failed [ failed_count ] . disk = disk ;
failed [ failed_count ] . file = 0 ;
failed [ failed_count ] . file_pos = 0 ;
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
2017-03-26 18:10:46 +02:00
/* here we are sure that the parity is used by a file */
used_parity = 1 ;
2014-10-14 21:37:28 +02:00
/* get the file of this block */
2015-10-03 10:43:02 +02:00
file = fs_par2file_get ( disk , i , & file_pos ) ;
2014-10-14 21:37:28 +02:00
2014-12-28 17:52:49 +01:00
/* if we are only hashing, we can skip excluded files and don't even read them */
2014-10-14 21:37:28 +02:00
if ( state - > opt . auditonly & & file_flag_has ( file , FILE_IS_EXCLUDED ) ) {
2012-10-07 23:18:10 +02:00
/* use an empty block */
2016-01-02 16:52:20 +01:00
/* in true, this is unnecessary, because we are not checking any parity */
2012-10-07 23:18:10 +02:00
/* 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 */
2014-10-14 21:37:28 +02:00
if ( handle [ j ] . file = = 0 | | handle [ j ] . file ! = file ) {
2013-04-28 19:01:36 +02:00
/* close the old one, if any */
2011-08-31 23:33:43 +02:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , i , disk - > name , esc_tag ( handle [ j ] . file - > sub , esc_buffer ) , strerror ( errno ) ) ;
2015-09-02 19:29:35 +02:00
log_fatal ( " DANGER! Unexpected close error in a data disk. \n " ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " Stopping at block %u \n " , i ) ;
2011-08-31 23:33:43 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-08-31 23:33:43 +02:00
}
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 */
2014-10-14 21:37:28 +02:00
if ( fix & & ! file_flag_has ( file , FILE_IS_EXCLUDED ) ) {
2013-04-28 19:01:36 +02:00
/* if fixing, create the file, open for writing and resize if required */
2014-10-14 21:37:28 +02:00
ret = handle_create ( & handle [ j ] , file , state - > file_mode ) ;
2013-04-28 19:01:36 +02:00
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2013-04-28 19:01:36 +02:00
if ( errno = = EACCES ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Please give write permission to the file. \n " ) ;
2013-04-28 19:01:36 +02:00
} else {
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2013-04-28 19:01:36 +02:00
}
2015-04-20 22:07:42 +02:00
log_fatal ( " Stopping at block %u \n " , i ) ;
2013-04-28 19:01:36 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
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 just created */
if ( handle [ j ] . created ! = 0 ) {
/* if fragmented, it may be reopened, so remember that the file */
/* was originally missing */
2014-10-14 21:37:28 +02:00
file_flag_set ( file , FILE_IS_CREATED ) ;
2013-04-28 19:01:36 +02:00
}
} else {
2015-10-15 19:34:58 +02:00
/* open the file only for reading */
if ( ! file_flag_has ( file , FILE_IS_MISSING ) )
ret = handle_open ( & handle [ j ] , file , state - > file_mode ,
log_error , state - > opt . expected_missing ? log_expected : 0 ) ;
else
ret = - 1 ; /* if the file is missing, we cannot open it */
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 ;
2015-10-03 10:43:02 +02:00
failed [ failed_count ] . disk = disk ;
failed [ failed_count ] . file = file ;
failed [ failed_count ] . file_pos = file_pos ;
2013-04-28 19:01:36 +02:00
failed [ failed_count ] . handle = & handle [ j ] ;
+ + failed_count ;
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Open error at position %u \n " , i , disk - > name , esc_tag ( file - > sub , esc_buffer ) , file_pos ) ;
2013-04-28 19:01:36 +02:00
+ + error ;
2015-10-15 19:34:58 +02:00
/* mark the file as missing, to avoid to retry to open it again */
/* note that this can be done only if we are not fixing it */
/* otherwise, it could be recreated */
file_flag_set ( file , FILE_IS_MISSING ) ;
2013-04-28 19:01:36 +02:00
continue ;
}
2014-12-28 19:11:58 +01:00
}
2011-04-28 21:38:25 +02:00
2014-12-29 08:50:20 +01:00
/* if it's the first open, and not excluded */
if ( ! file_flag_has ( file , FILE_IS_OPENED )
& & ! file_flag_has ( file , FILE_IS_EXCLUDED ) ) {
/* check if the file is changed */
2015-10-03 10:43:02 +02:00
if ( handle [ j ] . st . st_size ! = file - > size
| | handle [ j ] . st . st_mtime ! = file - > mtime_sec
| | STAT_NSEC ( & handle [ j ] . st ) ! = file - > mtime_nsec
2016-04-10 22:44:40 +02:00
/* don't check the inode to support file-system without persistent inodes */
2014-12-29 08:50:20 +01:00
) {
/* report that the file is not synced */
file_flag_set ( file , FILE_IS_UNSYNCED ) ;
}
}
/* if it's the first open, and not excluded and larger */
if ( ! file_flag_has ( file , FILE_IS_OPENED )
2014-12-28 19:11:58 +01:00
& & ! file_flag_has ( file , FILE_IS_EXCLUDED )
2014-12-29 08:50:20 +01:00
& & ! ( state - > opt . syncedonly & & file_flag_has ( file , FILE_IS_UNSYNCED ) )
2014-12-28 19:11:58 +01:00
& & handle [ j ] . st . st_size > file - > size
) {
2015-04-20 22:07:42 +02:00
log_error ( " File '%s' is larger than expected. \n " , handle [ j ] . path ) ;
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Size error \n " , i , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2014-12-28 19:11:58 +01:00
+ + error ;
if ( fix ) {
ret = handle_truncate ( & handle [ j ] , file ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected truncate error in a data disk, it isn't possible to fix. \n " ) ;
log_fatal ( " Stopping at block %u \n " , i ) ;
2014-12-28 19:11:58 +01:00
+ + unrecoverable_error ;
goto bail ;
/* LCOV_EXCL_STOP */
}
2011-04-28 21:38:25 +02:00
2016-05-29 18:19:13 +02:00
log_tag ( " fixed:%u:%s:%s: Fixed size \n " , i , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2014-12-28 19:11:58 +01:00
+ + recovered_error ;
2013-04-28 19:01:36 +02:00
}
2011-04-28 21:38:25 +02:00
}
2014-12-29 08:50:20 +01:00
/* mark the file as opened at least one time */
/* this is used to avoid to check the unsynced and size */
/* more than one time, in case the file is reopened later */
file_flag_set ( file , FILE_IS_OPENED ) ;
2011-03-04 20:59:21 +01:00
}
2012-10-07 23:18:10 +02:00
/* read from the file */
2015-10-03 10:43:02 +02:00
read_size = handle_read ( & handle [ j ] , file_pos , buffer [ j ] , state - > block_size ,
2015-04-20 22:07:42 +02:00
log_error , state - > opt . expected_missing ? log_expected : 0 ) ;
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 */
2014-02-01 17:57:26 +01:00
failed [ failed_count ] . is_bad = 1 ; /* it's bad because we cannot read it */
2012-02-09 23:17:36 +01:00
failed [ failed_count ] . is_outofdate = 0 ;
2011-06-11 12:05:02 +02:00
failed [ failed_count ] . index = j ;
failed [ failed_count ] . block = block ;
2015-10-03 10:43:02 +02:00
failed [ failed_count ] . disk = disk ;
failed [ failed_count ] . file = file ;
failed [ failed_count ] . file_pos = file_pos ;
2011-06-11 12:05:02 +02:00
failed [ failed_count ] . handle = & handle [ j ] ;
2011-04-23 21:38:26 +02:00
+ + failed_count ;
2011-03-04 20:59:21 +01:00
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Read error at position %u \n " , i , disk - > name , esc_tag ( file - > sub , esc_buffer ) , file_pos ) ;
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
2014-02-01 17:57:26 +01:00
/* always insert CHG blocks, the repair functions needs all of them */
/* because the parity may be still referring at the old state */
/* and the repair must be aware of it */
if ( block_state = = BLOCK_STATE_CHG ) {
/* we DO NOT mark them as bad to avoid to overwrite them with wrong data. */
/* if we don't have a hash, we always assume the first read of the block correct. */
failed [ failed_count ] . is_bad = 0 ; /* we assume the CHG block correct */
2012-02-09 23:17:36 +01:00
failed [ failed_count ] . is_outofdate = 0 ;
2011-06-11 12:05:02 +02:00
failed [ failed_count ] . index = j ;
failed [ failed_count ] . block = block ;
2015-10-03 10:43:02 +02:00
failed [ failed_count ] . disk = disk ;
failed [ failed_count ] . file = file ;
failed [ failed_count ] . file_pos = file_pos ;
2011-06-11 12:05:02 +02:00
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 ;
}
2014-02-01 17:57:26 +01:00
assert ( block_state = = BLOCK_STATE_BLK | | block_state = = BLOCK_STATE_REP ) ;
2012-02-09 23:17:36 +01:00
2014-02-01 17:57:26 +01:00
/* compute the hash of the block just read */
if ( rehash ) {
memhash ( state - > prevhash , state - > prevhashseed , hash , buffer [ j ] , read_size ) ;
} else {
memhash ( state - > hash , state - > hashseed , hash , buffer [ j ] , read_size ) ;
}
/* compare the hash */
2016-04-07 21:39:02 +02:00
if ( memcmp ( hash , block - > hash , BLOCK_HASH_SIZE ) ! = 0 ) {
unsigned diff = memdiff ( hash , block - > hash , BLOCK_HASH_SIZE ) ;
2015-09-12 15:19:52 +02:00
2014-02-01 17:57:26 +01:00
/* save the failed block for the check/fix */
failed [ failed_count ] . is_bad = 1 ; /* it's bad because the hash doesn't match */
failed [ failed_count ] . is_outofdate = 0 ;
failed [ failed_count ] . index = j ;
failed [ failed_count ] . block = block ;
2015-10-03 10:43:02 +02:00
failed [ failed_count ] . disk = disk ;
failed [ failed_count ] . file = file ;
failed [ failed_count ] . file_pos = file_pos ;
2014-02-01 17:57:26 +01:00
failed [ failed_count ] . handle = & handle [ j ] ;
+ + failed_count ;
2017-11-03 23:46:45 +01:00
log_tag ( " error:%u:%s:%s: Data error at position %u, diff bits %u/%u \n " , i , disk - > name , esc_tag ( file - > sub , esc_buffer ) , file_pos , diff , BLOCK_HASH_SIZE * 8 ) ;
2014-02-01 17:57:26 +01:00
+ + error ;
continue ;
}
/* always insert REP blocks, the repair functions needs all of them */
/* because the parity may be still referring at the old state */
/* and the repair must be aware of it */
if ( block_state = = BLOCK_STATE_REP ) {
failed [ failed_count ] . is_bad = 0 ; /* it's not bad */
failed [ failed_count ] . is_outofdate = 0 ;
failed [ failed_count ] . index = j ;
failed [ failed_count ] . block = block ;
2015-10-03 10:43:02 +02:00
failed [ failed_count ] . disk = disk ;
failed [ failed_count ] . file = file ;
failed [ failed_count ] . file_pos = file_pos ;
2014-02-01 17:57:26 +01:00
failed [ failed_count ] . handle = & handle [ j ] ;
+ + failed_count ;
continue ;
2012-02-09 23:17:36 +01:00
}
2011-03-04 20:59:21 +01:00
}
2012-10-07 23:18:10 +02:00
/* now read and check the parity if requested */
2014-02-12 22:04:14 +01:00
if ( ! state - > opt . auditonly ) {
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 */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l )
2013-09-18 21:56:06 +02:00
buffer_recov [ l ] = buffer [ diskmax + state - > level + l ] ;
2014-09-27 00:32:39 +02:00
for ( ; l < LEV_MAX ; + + l )
2013-09-18 21:56:06 +02:00
buffer_recov [ l ] = 0 ;
/* the zero buffer is the last one */
2014-09-27 00:32:39 +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 */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2013-09-15 21:12:41 +02:00
if ( parity [ l ] ) {
2015-04-20 22:07:42 +02:00
ret = parity_read ( parity [ l ] , i , buffer_recov [ l ] , state - > block_size , log_error ) ;
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
2015-04-20 22:07:42 +02:00
log_tag ( " 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 */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2012-02-09 23:17:36 +01:00
if ( failed [ j ] . is_bad )
2016-05-29 18:19:13 +02:00
log_tag ( " unrecoverable:%u:%s:%s: Unrecoverable error at position %u \n " , i , failed [ j ] . disk - > name , esc_tag ( failed [ j ] . file - > sub , esc_buffer ) , failed [ j ] . file_pos ) ;
2012-02-09 23:17:36 +01:00
}
2013-03-02 23:49:29 +01:00
/* keep track of damaged files */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2013-03-02 23:49:29 +01:00
if ( failed [ j ] . is_bad )
2015-10-03 10:43:02 +02:00
file_flag_set ( failed [ j ] . file , 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 */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2012-10-07 23:18:10 +02:00
if ( failed [ j ] . is_bad & & failed [ j ] . is_outofdate ) {
+ + partial_recover_error ;
2016-05-29 18:19:13 +02:00
log_tag ( " unrecoverable:%u:%s:%s: Unrecoverable unsynced error at position %u \n " , i , failed [ j ] . disk - > name , esc_tag ( failed [ j ] . file - > sub , esc_buffer ) , failed [ j ] . file_pos ) ;
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
2017-03-26 18:10:46 +02:00
/*
* Check parities , but only if all the blocks have it computed and it ' s used .
*
* If you check / fix after a partial sync , it ' s OK to have parity errors
* on the blocks with invalid parity and doesn ' t make sense to try to fix it .
*
* It ' s also OK to have data errors on unused parity , because sync doesn ' t
* update it .
*/
if ( used_parity & & valid_parity ) {
2012-10-07 23:18:10 +02:00
/* check the parity */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2013-09-15 21:12:41 +02:00
if ( buffer_recov [ l ] ! = 0 & & memcmp ( buffer_recov [ l ] , buffer [ diskmax + l ] , state - > block_size ) ! = 0 ) {
2015-09-12 15:19:52 +02:00
unsigned diff = memdiff ( buffer_recov [ l ] , buffer [ diskmax + l ] , state - > block_size ) ;
2015-10-17 17:36:40 +02:00
/* mark that the read parity is wrong, setting ptr to 0 */
2013-09-15 21:12:41 +02:00
buffer_recov [ l ] = 0 ;
2013-09-11 23:30:32 +02:00
2017-11-03 23:46:45 +01:00
log_tag ( " parity_error:%u:%s: Data error, diff bits %u/%u \n " , i , lev_config_name ( l ) , diff , state - > block_size * 8 ) ;
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
2015-10-07 22:07:20 +02:00
/* now write recovered files */
2012-10-07 23:18:10 +02:00
if ( fix ) {
/* update the fixed files */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2012-10-07 23:18:10 +02:00
/* nothing to do if it doesn't need recovering */
if ( ! failed [ j ] . is_bad )
continue ;
2011-08-31 23:33:43 +02:00
2014-12-29 08:50:20 +01:00
/* do not fix if the file is excluded */
2015-10-03 10:43:02 +02:00
if ( file_flag_has ( failed [ j ] . file , FILE_IS_EXCLUDED )
| | ( state - > opt . syncedonly & & file_flag_has ( failed [ j ] . file , FILE_IS_UNSYNCED ) ) )
2012-10-07 23:18:10 +02:00
continue ;
2011-05-05 21:12:42 +02:00
2015-10-03 10:43:02 +02:00
ret = handle_write ( failed [ j ] . handle , failed [ j ] . file_pos , buffer [ failed [ j ] . index ] , state - > block_size ) ;
2011-04-23 21:38:26 +02:00
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2012-10-07 23:18:10 +02:00
/* mark the file as damaged */
2015-10-03 10:43:02 +02:00
file_flag_set ( failed [ j ] . file , FILE_IS_DAMAGED ) ;
2012-10-07 23:18:10 +02:00
if ( errno = = EACCES ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Please give write permission to the file. \n " ) ;
2012-10-07 23:18:10 +02:00
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2012-10-07 23:18:10 +02:00
}
2015-04-20 22:07:42 +02:00
log_fatal ( " Stopping at block %u \n " , i ) ;
2011-04-23 21:38:26 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-04-23 21:38:26 +02:00
}
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 */
2015-10-03 10:43:02 +02:00
file_flag_set ( failed [ j ] . file , FILE_IS_DAMAGED ) ;
2012-10-07 23:18:10 +02:00
continue ;
}
2014-12-29 00:01:35 +01:00
/* mark the file as containing some fixes */
/* note that it could be also marked as damaged in other iterations */
2015-10-03 10:43:02 +02:00
file_flag_set ( failed [ j ] . file , FILE_IS_FIXED ) ;
2012-10-07 23:18:10 +02:00
2016-05-29 18:19:13 +02:00
log_tag ( " fixed:%u:%s:%s: Fixed data error at position %u \n " , i , failed [ j ] . disk - > name , esc_tag ( failed [ j ] . file - > sub , esc_buffer ) , failed [ j ] . file_pos ) ;
2011-04-23 21:38:26 +02:00
+ + recovered_error ;
}
2017-03-26 18:10:46 +02:00
/*
* Update parity only if all the blocks have it computed and it ' s used .
*
* 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 .
*
* For unused parity there is no need to write it , because when fixing
* we already have allocated space for it on parity file creation ,
* and its content doesn ' t matter .
*/
if ( used_parity & & valid_parity ) {
2012-10-07 23:18:10 +02:00
/* update the parity */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2015-10-17 17:36:40 +02:00
/* if the parity on disk is wrong */
if ( buffer_recov [ l ] = = 0
2016-04-10 22:44:40 +02:00
/* and we have access at the parity */
2015-10-17 17:36:40 +02:00
& & parity [ l ] ! = 0
2016-04-10 22:44:40 +02:00
/* and the parity is not excluded */
2017-03-26 18:10:46 +02:00
& & ! state - > parity [ l ] . is_excluded_by_filter
2015-10-17 17:36:40 +02:00
) {
2013-09-15 21:12:41 +02:00
ret = parity_write ( parity [ l ] , i , buffer [ diskmax + l ] , state - > block_size ) ;
2013-09-11 23:30:32 +02:00
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2013-09-11 23:30:32 +02:00
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working %s disk, it isn't possible to fix errors on it. \n " , lev_name ( l ) ) ;
log_fatal ( " Stopping at block %u \n " , i ) ;
2013-09-11 23:30:32 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-09-11 23:30:32 +02:00
}
2015-04-20 22:07:42 +02:00
log_tag ( " 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 */
2014-12-28 17:52:49 +01:00
/* meaning that we could fix this file if we try */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2013-03-02 23:49:29 +01:00
if ( failed [ j ] . is_bad ) {
2015-10-03 10:43:02 +02:00
file_flag_set ( failed [ j ] . file , FILE_IS_FIXED ) ;
2013-03-02 23:49:29 +01:00
}
}
}
}
} else {
/* if we are not checking, we just set the DAMAGED flag */
2014-12-28 17:52:49 +01:00
/* to report that the file is damaged, and we don't know if we can fix it */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2013-03-02 23:49:29 +01:00
if ( failed [ j ] . is_bad ) {
2015-10-03 10:43:02 +02:00
file_flag_set ( failed [ j ] . file , FILE_IS_DAMAGED ) ;
2011-04-23 21:38:26 +02:00
}
2011-03-04 20:59:21 +01:00
}
}
2014-12-29 08:50:20 +01:00
/* post process the files */
ret = file_post ( state , fix , i , handle , diskmax ) ;
if ( ret = = - 1 ) {
2016-11-06 18:27:44 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " Stopping at block %u \n " , i ) ;
2014-12-29 08:50:20 +01:00
+ + unrecoverable_error ;
goto bail ;
2016-11-06 18:27:44 +01:00
/* LCOV_EXCL_STOP */
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 */
2016-01-27 20:37:31 +01:00
if ( state_progress ( state , 0 , i , countpos , countmax , countsize ) ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2011-03-04 20:59:21 +01:00
break ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-03-18 22:38:11 +01:00
}
2025-09-21 19:54:10 +02:00
/* thermal control */
if ( state_thermal_alarm ( state ) ) {
/* until now is misc */
state_usage_misc ( state ) ;
state_progress_stop ( state ) ;
state_thermal_cooldown ( state ) ;
state_progress_restart ( state ) ;
/* drop until now */
state_usage_waste ( state ) ;
}
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 */
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < diskmax ; + + i ) {
2011-10-15 08:39:52 +02:00
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 ;
2020-05-02 14:20:47 +02:00
int unsuccessful = 0 ;
2011-10-15 08:39:52 +02:00
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 ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2011-10-15 08:39:52 +02:00
2015-04-20 22:07:42 +02:00
log_error ( " Error stating empty file '%s'. %s. \n " , path , strerror ( errno ) ) ;
2025-12-11 12:49:44 +01:00
log_tag ( " empty_error:%s:%s: Empty file stat error \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2011-10-15 08:39:52 +02:00
+ + error ;
} else if ( ! S_ISREG ( st . st_mode ) ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2011-10-15 08:39:52 +02:00
2025-12-11 12:49:44 +01:00
log_tag ( " empty_error:%s:%s: Empty file error for not regular file \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2011-10-15 08:39:52 +02:00
+ + error ;
} else if ( st . st_size ! = 0 ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2011-10-15 08:39:52 +02:00
2025-12-11 12:49:44 +01:00
log_tag ( " empty_error:%s:%s: Empty file error for size '% " PRIu64 " ' \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) , ( uint64_t ) st . st_size ) ;
2011-10-15 08:39:52 +02:00
+ + error ;
}
2020-05-02 14:20:47 +02:00
if ( fix & & unsuccessful ) {
2011-10-15 08:39:52 +02:00
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 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
log_fatal ( " Stopping \n " ) ;
2011-10-15 08:39:52 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-10-15 08:39:52 +02:00
}
/* 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 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " Error creating empty file '%s'. %s. \n " , path , strerror ( errno ) ) ;
2011-10-15 08:39:52 +02:00
if ( errno = = EACCES ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Please give write permission to the file. \n " ) ;
2011-10-15 08:39:52 +02:00
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2011-10-15 08:39:52 +02:00
}
2015-04-20 22:07:42 +02:00
log_fatal ( " Stopping \n " ) ;
2011-10-15 08:39:52 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-10-15 08:39:52 +02:00
}
2013-08-26 20:20:36 +02:00
/* set the original modification time */
2014-05-18 15:11:18 +02:00
ret = fmtime ( f , file - > mtime_sec , file - > mtime_nsec ) ;
2013-08-26 20:20:36 +02:00
if ( ret ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2013-08-26 20:20:36 +02:00
close ( f ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " Error timing file '%s'. %s. \n " , file - > sub , strerror ( errno ) ) ;
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
log_fatal ( " Stopping \n " ) ;
2013-08-26 20:20:36 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-08-26 20:20:36 +02:00
}
2011-10-15 08:39:52 +02:00
/* close it */
2013-08-26 20:20:36 +02:00
ret = close ( f ) ;
if ( ret ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
log_fatal ( " Stopping \n " ) ;
2013-08-26 20:20:36 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-08-26 20:20:36 +02:00
}
2011-10-15 08:39:52 +02:00
2025-12-11 12:49:44 +01:00
log_tag ( " empty_fixed:%s:%s: Fixed empty file \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2011-10-15 08:39:52 +02:00
+ + recovered_error ;
2013-03-01 20:13:56 +01:00
2016-05-29 18:19:13 +02:00
log_tag ( " status:recovered:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " recovered %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) ) ;
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 ;
2015-10-08 22:19:52 +02:00
struct snapraid_link * slink ;
2020-05-02 14:20:47 +02:00
int unsuccessful = 0 ;
2013-03-01 18:48:13 +01:00
int unrecoverable = 0 ;
2011-08-26 14:18:38 +02:00
2015-10-08 22:19:52 +02:00
slink = node - > data ;
2011-08-26 14:18:38 +02:00
node = node - > next ; /* next node */
/* if excluded continue to the next one */
2015-10-08 22:19:52 +02:00
if ( link_flag_has ( slink , FILE_IS_EXCLUDED ) ) {
2011-08-26 14:18:38 +02:00
continue ;
}
2015-10-08 22:19:52 +02:00
if ( link_flag_has ( slink , FILE_IS_HARDLINK ) ) {
2013-01-27 18:06:15 +01:00
/* stat the link */
2015-10-08 22:19:52 +02:00
pathprint ( path , sizeof ( path ) , " %s%s " , disk - > dir , slink - > sub ) ;
2013-01-27 18:06:15 +01:00
ret = stat ( path , & st ) ;
if ( ret = = - 1 ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2011-08-26 14:18:38 +02:00
2015-04-20 22:07:42 +02:00
log_error ( " Error stating hardlink '%s'. %s. \n " , path , strerror ( errno ) ) ;
2016-05-29 18:19:13 +02:00
log_tag ( " hardlink_error:%s:%s:%s: Hardlink stat error \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) , esc_tag ( slink - > linkto , esc_buffer_alt ) ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
} else if ( ! S_ISREG ( st . st_mode ) ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2011-08-26 14:18:38 +02:00
2016-05-29 18:19:13 +02:00
log_tag ( " hardlink_error:%s:%s:%s: Hardlink error for not regular file \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) , esc_tag ( slink - > linkto , esc_buffer_alt ) ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
}
/* stat the "to" file */
2015-10-08 22:19:52 +02:00
pathprint ( pathto , sizeof ( pathto ) , " %s%s " , disk - > dir , slink - > linkto ) ;
2013-01-27 18:06:15 +01:00
ret = stat ( pathto , & stto ) ;
if ( ret = = - 1 ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2013-01-27 18:06:15 +01:00
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
}
2015-04-20 22:07:42 +02:00
log_error ( " Error stating hardlink-to '%s'. %s. \n " , pathto , strerror ( errno ) ) ;
2016-05-29 18:19:13 +02:00
log_tag ( " hardlink_error:%s:%s:%s: Hardlink to stat error \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) , esc_tag ( slink - > linkto , esc_buffer_alt ) ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
2013-02-24 19:02:53 +01:00
} else if ( ! S_ISREG ( stto . st_mode ) ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2013-01-27 18:06:15 +01:00
2016-05-29 18:19:13 +02:00
log_tag ( " hardlink_error:%s:%s:%s: Hardlink-to error for not regular file \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) , esc_tag ( slink - > linkto , esc_buffer_alt ) ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
2020-05-02 14:20:47 +02:00
} else if ( ! unsuccessful & & st . st_ino ! = stto . st_ino ) {
unsuccessful = 1 ;
2013-01-27 18:06:15 +01:00
2015-04-20 22:07:42 +02:00
log_error ( " Mismatch hardlink '%s' and '%s'. Different inode. \n " , path , pathto ) ;
2016-05-29 18:19:13 +02:00
log_tag ( " hardlink_error:%s:%s:%s: Hardlink mismatch for different inode \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) , esc_tag ( slink - > linkto , esc_buffer_alt ) ) ;
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 */
2015-10-08 22:19:52 +02:00
pathprint ( path , sizeof ( path ) , " %s%s " , disk - > dir , slink - > sub ) ;
2013-01-27 18:06:15 +01:00
ret = readlink ( path , linkto , sizeof ( linkto ) ) ;
if ( ret < 0 ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2011-08-26 14:18:38 +02:00
2015-04-20 22:07:42 +02:00
log_error ( " Error reading symlink '%s'. %s. \n " , path , strerror ( errno ) ) ;
2016-05-29 18:19:13 +02:00
log_tag ( " symlink_error:%s:%s: Symlink read error \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) ) ;
2011-08-26 14:18:38 +02:00
+ + error ;
2013-02-21 18:50:27 +01:00
} else if ( ret > = PATH_MAX ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2013-01-27 18:06:15 +01:00
2015-04-20 22:07:42 +02:00
log_error ( " Error reading symlink '%s'. Symlink too long. \n " , path ) ;
2016-05-29 18:19:13 +02:00
log_tag ( " symlink_error:%s:%s: Symlink read error \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
} else {
linkto [ ret ] = 0 ;
2015-10-08 22:19:52 +02:00
if ( strcmp ( linkto , slink - > linkto ) ! = 0 ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2013-01-27 18:06:15 +01:00
2016-05-29 18:19:13 +02:00
log_tag ( " symlink_error:%s:%s: Symlink data error '%s' instead of '%s' \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) , linkto , slink - > linkto ) ;
2013-01-27 18:06:15 +01:00
+ + error ;
}
2011-08-26 14:18:38 +02:00
}
}
2020-05-02 14:20:47 +02:00
if ( fix & & unsuccessful & & ! 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 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
log_fatal ( " Stopping \n " ) ;
2011-08-26 14:18:38 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-08-26 14:18:38 +02:00
}
/* if it exists, it must be deleted before recreating */
ret = remove ( path ) ;
if ( ret ! = 0 & & errno ! = ENOENT ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " Error removing '%s'. %s. \n " , path , strerror ( errno ) ) ;
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
log_fatal ( " Stopping \n " ) ;
2011-08-26 14:18:38 +02:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-08-26 14:18:38 +02:00
}
/* create it */
2015-10-08 22:19:52 +02:00
if ( link_flag_has ( slink , FILE_IS_HARDLINK ) ) {
2013-01-27 18:06:15 +01:00
ret = hardlink ( pathto , path ) ;
if ( ret ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " Error writing hardlink '%s' to '%s'. %s. \n " , path , pathto , strerror ( errno ) ) ;
2013-01-27 18:06:15 +01:00
if ( errno = = EACCES ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Please give write permission to the hardlink. \n " ) ;
2013-01-27 18:06:15 +01:00
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2013-01-27 18:06:15 +01:00
}
2015-04-20 22:07:42 +02:00
log_fatal ( " Stopping \n " ) ;
2013-01-27 18:06:15 +01:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-08-26 14:18:38 +02:00
}
2016-05-29 18:19:13 +02:00
log_tag ( " hardlink_fixed:%s:%s: Fixed hardlink error \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) ) ;
2013-01-27 18:06:15 +01:00
+ + recovered_error ;
} else {
2015-10-08 22:19:52 +02:00
ret = symlink ( slink - > linkto , path ) ;
2013-01-27 18:06:15 +01:00
if ( ret ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-10-08 22:19:52 +02:00
log_fatal ( " Error writing symlink '%s' to '%s'. %s. \n " , path , slink - > linkto , strerror ( errno ) ) ;
2013-01-27 18:06:15 +01:00
if ( errno = = EACCES ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Please give write permission to the symlink. \n " ) ;
2013-01-27 18:06:15 +01:00
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2013-01-27 18:06:15 +01:00
}
2015-04-20 22:07:42 +02:00
log_fatal ( " Stopping \n " ) ;
2013-01-27 18:06:15 +01:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-01-27 18:06:15 +01:00
}
2016-05-29 18:19:13 +02:00
log_tag ( " symlink_fixed:%s:%s: Fixed symlink error \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) ) ;
2013-01-27 18:06:15 +01:00
+ + recovered_error ;
}
2013-03-01 20:13:56 +01:00
2016-05-29 18:19:13 +02:00
log_tag ( " status:recovered:%s:%s \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " recovered %s \n " , fmt_term ( disk , slink - > sub , esc_buffer ) ) ;
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 ;
2020-05-02 14:20:47 +02:00
int unsuccessful = 0 ;
2013-01-27 16:44:04 +01:00
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 ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2013-01-27 16:44:04 +01:00
2015-04-20 22:07:42 +02:00
log_error ( " Error stating dir '%s'. %s. \n " , path , strerror ( errno ) ) ;
2016-05-29 18:19:13 +02:00
log_tag ( " dir_error:%s:%s: Dir stat error \n " , disk - > name , esc_tag ( dir - > sub , esc_buffer ) ) ;
2013-01-27 16:44:04 +01:00
+ + error ;
} else if ( ! S_ISDIR ( st . st_mode ) ) {
2020-05-02 14:20:47 +02:00
unsuccessful = 1 ;
2013-01-27 16:44:04 +01:00
2016-05-29 18:19:13 +02:00
log_tag ( " dir_error:%s:%s: Dir error for not directory \n " , disk - > name , esc_tag ( dir - > sub , esc_buffer ) ) ;
2013-01-27 16:44:04 +01:00
+ + error ;
}
2020-05-02 14:20:47 +02:00
if ( fix & & unsuccessful ) {
2013-01-27 16:44:04 +01:00
/* 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 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
log_fatal ( " Stopping \n " ) ;
2013-01-27 16:44:04 +01:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-01-27 16:44:04 +01:00
}
/* create it */
ret = mkdir ( path , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) ;
if ( ret ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " Error creating dir '%s'. %s. \n " , path , strerror ( errno ) ) ;
2013-01-27 16:44:04 +01:00
if ( errno = = EACCES ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Please give write permission to the dir. \n " ) ;
2013-01-27 16:44:04 +01:00
} else {
/* we do not use DANGER because it could be ENOSPC which is not always correctly reported */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2013-01-27 16:44:04 +01:00
}
2015-04-20 22:07:42 +02:00
log_fatal ( " Stopping \n " ) ;
2013-01-27 16:44:04 +01:00
+ + unrecoverable_error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-01-27 16:44:04 +01:00
}
2016-05-29 18:19:13 +02:00
log_tag ( " dir_fixed:%s:%s: Fixed dir error \n " , disk - > name , esc_tag ( dir - > sub , esc_buffer ) ) ;
2013-01-27 16:44:04 +01:00
+ + recovered_error ;
2013-03-01 20:13:56 +01:00
2016-05-29 18:19:13 +02:00
log_tag ( " status:recovered:%s:%s \n " , disk - > name , esc_tag ( dir - > sub , esc_buffer ) ) ;
2016-05-29 19:23:42 +02:00
msg_info ( " recovered %s \n " , fmt_term ( disk , dir - > sub , esc_buffer ) ) ;
2011-08-26 14:18:38 +02:00
}
}
}
2025-09-24 11:32:10 +02:00
end :
2025-10-13 11:46:36 +02:00
state_progress_end ( state , countpos , countmax , countsize , " Nothing to check. \n " ) ;
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 */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
2015-09-02 19:29:35 +02:00
struct snapraid_file * file = handle [ j ] . file ;
struct snapraid_disk * disk = handle [ j ] . disk ;
2013-05-31 23:34:59 +02:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2016-05-29 18:19:13 +02:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , blockmax , disk - > name , esc_tag ( file - > sub , esc_buffer ) , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected close error in a data disk. \n " ) ;
2013-05-31 23:34:59 +02:00
+ + unrecoverable_error ;
/* continue, as we are already exiting */
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-05-31 23:34:59 +02:00
}
}
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 */
2014-12-29 00:01:35 +01:00
/* it happens only when aborting pressing Ctrl+C or other reason. */
2013-05-31 23:34:59 +02:00
if ( fix ) {
/* for each disk */
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < diskmax ; + + i ) {
2013-05-31 23:34:59 +02:00
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 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " Error removing '%s'. %s. \n " , path , strerror ( errno ) ) ;
log_fatal ( " WARNING! Without a working data disk, it isn't possible to fix errors on it. \n " ) ;
2013-04-28 19:01:36 +02:00
+ + unrecoverable_error ;
/* continue, as we are already exiting */
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-04-28 19:01:36 +02:00
}
}
2011-03-18 22:38:11 +01:00
}
}
2011-03-17 16:17:59 +01:00
if ( error | | recovered_error | | unrecoverable_error ) {
2015-02-22 14:31:03 +01:00
msg_status ( " \n " ) ;
msg_status ( " %8u errors \n " , error ) ;
2011-03-17 16:17:59 +01:00
if ( fix ) {
2015-02-22 14:31:03 +01:00
msg_status ( " %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 ) {
2015-02-22 14:31:03 +01:00
msg_status ( " %8u UNRECOVERABLE errors \n " , unrecoverable_error ) ;
2013-10-19 13:24:35 +02:00
} else {
2012-10-07 23:18:10 +02:00
/* without checking, we don't know if they are really recoverable or not */
2014-02-12 22:04:14 +01:00
if ( ! state - > opt . auditonly )
2015-02-22 14:31:03 +01:00
msg_status ( " %8u unrecoverable errors \n " , unrecoverable_error ) ;
2013-10-19 13:24:35 +02:00
if ( fix )
2015-02-22 14:31:03 +01:00
msg_status ( " Everything OK \n " ) ;
2012-10-07 23:18:10 +02:00
}
2011-03-04 20:59:21 +01:00
} else {
2015-02-22 14:31:03 +01:00
msg_status ( " Everything OK \n " ) ;
2011-03-04 20:59:21 +01:00
}
2015-02-22 14:31:03 +01:00
if ( error & & ! fix )
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! There are errors! \n " ) ;
2015-02-22 14:31:03 +01:00
if ( unrecoverable_error )
2023-12-09 12:07:28 +01:00
log_fatal ( " DANGER! Unrecoverable errors detected! \n " ) ;
2015-02-22 14:31:03 +01:00
2015-04-20 22:07:42 +02:00
log_tag ( " summary:error:%u \n " , error ) ;
2013-10-03 22:44:33 +02:00
if ( fix )
2015-04-20 22:07:42 +02:00
log_tag ( " summary:error_recovered:%u \n " , recovered_error ) ;
2014-02-12 22:04:14 +01:00
if ( ! state - > opt . auditonly )
2015-04-20 22:07:42 +02:00
log_tag ( " summary:error_unrecoverable:%u \n " , unrecoverable_error ) ;
2013-10-03 22:44:33 +02:00
if ( fix ) {
if ( error + recovered_error + unrecoverable_error = = 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " summary:exit:ok \n " ) ;
2013-10-03 22:44:33 +02:00
else if ( unrecoverable_error = = 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " summary:exit:recovered \n " ) ;
2013-10-03 22:44:33 +02:00
else
2015-04-20 22:07:42 +02:00
log_tag ( " summary:exit:unrecoverable \n " ) ;
2014-02-12 22:04:14 +01:00
} else if ( ! state - > opt . auditonly ) {
2013-10-03 22:44:33 +02:00
if ( error + unrecoverable_error = = 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " summary:exit:ok \n " ) ;
2013-10-03 22:44:33 +02:00
else if ( unrecoverable_error = = 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " summary:exit:recoverable \n " ) ;
2013-10-03 22:44:33 +02:00
else
2015-04-20 22:07:42 +02:00
log_tag ( " summary:exit:unrecoverable \n " ) ;
2013-10-03 22:44:33 +02:00
} else { /* audit only */
if ( error = = 0 )
2015-04-20 22:07:42 +02:00
log_tag ( " summary:exit:ok \n " ) ;
2013-10-03 22:44:33 +02:00
else
2015-04-20 22:07:42 +02:00
log_tag ( " summary:exit:error \n " ) ;
2013-09-30 22:24:30 +02:00
}
2015-04-20 22:07:42 +02:00
log_flush ( ) ;
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 ) ;
2021-05-22 11:37:36 +02:00
free ( block_enabled ) ;
2011-03-04 20:59:21 +01:00
free ( handle ) ;
2011-04-23 21:38:26 +02:00
free ( buffer_alloc ) ;
free ( buffer ) ;
2015-10-07 22:07:20 +02:00
/* fail if some error are present after the run */
2011-04-23 21:38:26 +02:00
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
}
2014-02-12 22:04:14 +01:00
int state_check ( struct snapraid_state * state , 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 ;
2011-03-04 20:59:21 +01:00
int ret ;
2014-11-09 10:25:39 +01:00
struct snapraid_parity_handle parity [ LEV_MAX ] ;
struct snapraid_parity_handle * 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
2015-02-22 14:31:03 +01:00
msg_progress ( " Initializing... \n " ) ;
2011-03-04 20:59:21 +01:00
2015-02-01 18:48:50 +01:00
blockmax = parity_allocated_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 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2023-12-09 12:07:28 +01:00
log_fatal ( " Error in the specified starting block %u. It's larger than the parity size %u. \n " , blockstart , blockmax ) ;
2011-03-04 20:59:21 +01:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-03-04 20:59:21 +01:00
}
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 */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2015-10-16 23:17:38 +02:00
/* skip parity disks that are not accessible */
if ( state - > parity [ l ] . skip_access ) {
parity_ptr [ l ] = 0 ;
continue ;
}
2013-09-15 21:12:41 +02:00
parity_ptr [ l ] = & parity [ l ] ;
2013-09-11 23:30:32 +02:00
2019-05-19 16:46:03 +02:00
/* if the parity is excluded */
if ( state - > parity [ l ] . is_excluded_by_filter ) {
/* open for reading, and ignore error */
ret = parity_open ( parity_ptr [ l ] , & state - > parity [ l ] , l , state - > file_mode , state - > block_size , state - > opt . parity_limit_size ) ;
if ( ret = = - 1 ) {
/* continue anyway */
parity_ptr [ l ] = 0 ;
}
} else {
/* open for writing */
ret = parity_create ( parity_ptr [ l ] , & state - > parity [ l ] , l , state - > file_mode , state - > block_size , state - > opt . parity_limit_size ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
log_fatal ( " WARNING! Without an accessible %s file, it isn't possible to fix any error. \n " , lev_name ( l ) ) ;
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
ret = parity_chsize ( parity_ptr [ l ] , & state - > parity [ l ] , 0 , size , state - > block_size , state - > opt . skip_fallocate , state - > opt . skip_space_holder ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
log_fatal ( " WARNING! Without an accessible %s file, it isn't possible to sync. \n " , lev_name ( l ) ) ;
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
2013-09-11 23:30:32 +02:00
}
}
2014-02-12 22:04:14 +01:00
} else if ( ! state - > opt . auditonly ) {
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 */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2013-09-15 21:12:41 +02:00
parity_ptr [ l ] = & parity [ l ] ;
2016-09-28 20:32:50 +02:00
ret = parity_open ( parity_ptr [ l ] , & state - > parity [ l ] , l , state - > file_mode , state - > block_size , state - > opt . parity_limit_size ) ;
2011-10-09 07:51:25 +02:00
if ( ret = = - 1 ) {
2015-02-22 14:31:03 +01:00
msg_status ( " 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 */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l )
2013-09-15 21:12:41 +02:00
parity_ptr [ l ] = 0 ;
2011-03-04 20:59:21 +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 ) {
2014-02-12 22:04:14 +01:00
ret = state_check_process ( state , fix , parity_ptr , blockstart , blockmax ) ;
2011-03-17 16:17:59 +01:00
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2011-04-23 21:38:26 +02:00
+ + error ;
2011-03-19 22:00:08 +01:00
/* continue, as we are already exiting */
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
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 */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2013-09-15 21:12:41 +02:00
if ( parity_ptr [ l ] ) {
2019-05-21 22:33:24 +02:00
/* if fixing and not excluded, truncate parity not valid */
if ( fix & & ! state - > parity [ l ] . is_excluded_by_filter ) {
ret = parity_truncate ( parity_ptr [ l ] ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
log_fatal ( " DANGER! Unexpected truncate error in %s disk. \n " , lev_name ( l ) ) ;
+ + error ;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
}
2013-09-15 21:12:41 +02:00
ret = parity_close ( parity_ptr [ l ] ) ;
2013-09-11 23:30:32 +02:00
if ( ret = = - 1 ) {
2019-05-21 22:33:24 +02:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " 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 */
2019-05-21 22:33:24 +02:00
/* LCOV_EXCL_STOP */
2013-09-11 23:30:32 +02:00
}
}
}
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
}
2025-10-26 18:06:08 +01:00