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 "elem.h"
# include "state.h"
# include "parity.h"
# include "handle.h"
2015-12-30 23:45:11 +01:00
# include "io.h"
2013-12-28 09:10:00 +01:00
# include "raid/raid.h"
2011-03-04 20:59:21 +01:00
2014-02-02 23:03:37 +01:00
/****************************************************************************/
/* hash */
2014-12-27 23:14:06 +01:00
static int state_hash_process ( struct snapraid_state * state , block_off_t blockstart , block_off_t blockmax , int * skip_sync )
2014-02-02 23:03:37 +01:00
{
struct snapraid_handle * handle ;
unsigned diskmax ;
block_off_t i ;
unsigned j ;
void * buffer ;
data_off_t countsize ;
block_off_t countpos ;
block_off_t countmax ;
int ret ;
unsigned error ;
2016-01-10 22:04:16 +01:00
unsigned silent_error ;
2014-12-27 23:14:06 +01:00
unsigned io_error ;
2014-02-02 23:03:37 +01:00
/* maps the disks to handles */
2016-01-18 22:38:01 +01:00
handle = handle_mapping ( state , & diskmax ) ;
2014-02-02 23:03:37 +01:00
/* buffer for reading */
buffer = malloc_nofail ( state - > block_size ) ;
2014-02-03 19:05:22 +01:00
if ( ! state - > opt . skip_self )
mtest_vector ( 1 , state - > block_size , & buffer ) ;
2014-02-02 23:03:37 +01:00
error = 0 ;
2016-01-10 22:04:16 +01:00
silent_error = 0 ;
2014-12-27 23:14:06 +01:00
io_error = 0 ;
2014-02-02 23:03:37 +01:00
/* first count the number of blocks to process */
countmax = 0 ;
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
2014-02-02 23:03:37 +01:00
struct snapraid_disk * disk = handle [ j ] . disk ;
2014-12-29 19:11:03 +01:00
/* if no disk, nothing to check */
2014-02-02 23:03:37 +01:00
if ( ! disk )
continue ;
2014-04-04 23:06:00 +02:00
2014-09-27 00:32:39 +02:00
for ( i = blockstart ; i < blockmax ; + + i ) {
2014-02-02 23:03:37 +01:00
struct snapraid_block * block ;
2015-09-29 22:31:13 +02:00
unsigned block_state ;
2014-02-02 23:03:37 +01:00
2016-01-16 14:26:05 +01:00
block = fs_par2block_find ( disk , i ) ;
2014-02-02 23:03:37 +01:00
2015-09-29 22:31:13 +02:00
/* get the state of the block */
block_state = block_state_get ( block ) ;
2014-02-02 23:03:37 +01:00
2015-09-29 22:31:13 +02:00
/* process REP and CHG blocks */
if ( block_state ! = BLOCK_STATE_REP & & block_state ! = BLOCK_STATE_CHG )
2014-02-02 23:03:37 +01:00
continue ;
+ + countmax ;
}
}
2014-06-25 22:32:36 +02:00
/* drop until now */
state_usage_waste ( state ) ;
2014-02-02 23:03:37 +01:00
countsize = 0 ;
countpos = 0 ;
2014-09-27 00:23:05 +02:00
if ( ! state_progress_begin ( state , blockstart , blockmax , countmax ) )
goto end ;
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
2014-02-02 23:03:37 +01:00
struct snapraid_disk * disk = handle [ j ] . disk ;
2014-12-29 19:11:03 +01:00
/* if no disk, nothing to check */
2014-02-02 23:03:37 +01:00
if ( ! disk )
continue ;
2014-09-27 00:32:39 +02:00
for ( i = blockstart ; i < blockmax ; + + i ) {
2014-02-02 23:03:37 +01:00
snapraid_info info ;
int rehash ;
struct snapraid_block * block ;
int read_size ;
unsigned char hash [ HASH_SIZE ] ;
2015-09-29 22:31:13 +02:00
unsigned block_state ;
2015-10-03 10:43:02 +02:00
struct snapraid_file * file ;
block_off_t file_pos ;
2014-02-02 23:03:37 +01:00
2016-01-16 14:26:05 +01:00
block = fs_par2block_find ( disk , i ) ;
2014-02-02 23:03:37 +01:00
2015-09-29 22:31:13 +02:00
/* get the state of the block */
block_state = block_state_get ( block ) ;
2014-02-02 23:03:37 +01:00
2015-09-29 22:31:13 +02:00
/* process REP and CHG blocks */
if ( block_state ! = BLOCK_STATE_REP & & block_state ! = BLOCK_STATE_CHG )
2014-02-02 23:03:37 +01:00
continue ;
2015-10-03 10:43:02 +02:00
/* get the file of this block */
file = fs_par2file_get ( disk , i , & file_pos ) ;
2014-02-02 23:03:37 +01:00
/* get block specific info */
info = info_get ( & state - > infoarr , i ) ;
/* if we have to use the old hash */
rehash = info_get_rehash ( info ) ;
2016-01-26 23:25:01 +01:00
/* until now is misc */
state_usage_misc ( state ) ;
2014-06-25 22:32:36 +02:00
2014-02-02 23:03:37 +01:00
/* if the file is different than the current one, close it */
2015-10-03 10:43:02 +02:00
if ( handle [ j ] . file ! = 0 & & handle [ j ] . file ! = file ) {
2014-12-27 23:14:06 +01:00
/* keep a pointer at the file we are going to close for error reporting */
2015-10-03 10:43:02 +02:00
struct snapraid_file * report = handle [ j ] . file ;
2014-02-02 23:03:37 +01:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2014-02-02 23:03:37 +01:00
/* This one is really an unexpected error, because we are only reading */
/* and closing a descriptor should never fail */
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
2015-10-03 10:43:02 +02:00
log_tag ( " error:%u:%s:%s: Close EIO error. %s \n " , i , disk - > name , esc ( report - > sub ) , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle [ j ] . path ) ;
log_fatal ( " Stopping at block %u \n " , i ) ;
2014-12-27 23:14:06 +01:00
+ + io_error ;
goto bail ;
}
2015-10-03 10:43:02 +02:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , i , disk - > name , esc ( report - > sub ) , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Unexpected close error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that file '%s' can be accessed. \n " , handle [ j ] . path ) ;
log_fatal ( " Stopping at block %u \n " , i ) ;
2014-02-02 23:03:37 +01:00
+ + error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2014-02-02 23:03:37 +01:00
}
}
2015-10-03 10:43:02 +02:00
ret = handle_open ( & handle [ j ] , file , state - > file_mode , log_error , 0 ) ;
2014-02-02 23:03:37 +01:00
if ( ret = = - 1 ) {
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_tag ( " error:%u:%s:%s: Open EIO error. %s \n " , i , disk - > name , esc ( file - > sub ) , strerror ( errno ) ) ;
log_fatal ( " DANGER! Unexpected input/output open error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle [ j ] . path ) ;
log_fatal ( " Stopping at block %u \n " , i ) ;
2014-12-27 23:14:06 +01:00
+ + io_error ;
goto bail ;
/* LCOV_EXCL_STOP */
}
2014-02-02 23:03:37 +01:00
if ( errno = = ENOENT ) {
2015-04-20 22:07:42 +02:00
log_tag ( " error:%u:%s:%s: Open ENOENT error. %s \n " , i , disk - > name , esc ( file - > sub ) , strerror ( errno ) ) ;
log_error ( " Missing file '%s'. \n " , handle [ j ] . path ) ;
log_error ( " WARNING! You cannot modify data disk during a sync. \n " ) ;
log_error ( " Rerun the sync command when finished. \n " ) ;
2014-02-02 23:03:37 +01:00
+ + error ;
/* if the file is missing, it means that it was removed during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
continue ;
2014-04-07 23:12:40 +02:00
}
2014-12-27 23:14:06 +01:00
2014-04-07 23:12:40 +02:00
if ( errno = = EACCES ) {
2015-04-20 22:07:42 +02:00
log_tag ( " error:%u:%s:%s: Open EACCES error. %s \n " , i , disk - > name , esc ( file - > sub ) , strerror ( errno ) ) ;
log_error ( " No access at file '%s'. \n " , handle [ j ] . path ) ;
log_error ( " WARNING! Please fix the access permission in the data disk. \n " ) ;
log_error ( " Rerun the sync command when finished. \n " ) ;
2014-02-02 23:03:37 +01:00
+ + error ;
/* this isn't a serious error, so we skip this block, and continue with others */
continue ;
}
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_tag ( " error:%u:%s:%s: Open error. %s \n " , i , disk - > name , esc ( file - > sub ) , strerror ( errno ) ) ;
log_fatal ( " WARNING! Unexpected open error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that file '%s' can be accessed. \n " , handle [ j ] . path ) ;
log_fatal ( " Stopping to allow recovery. Try with 'snapraid check -f %s' \n " , file - > sub ) ;
2014-02-02 23:03:37 +01:00
+ + error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2014-02-02 23:03:37 +01:00
}
/* 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
| | handle [ j ] . st . st_ino ! = file - > inode
2014-02-02 23:03:37 +01:00
) {
2015-04-20 22:07:42 +02:00
log_tag ( " error:%u:%s:%s: Unexpected attribute change \n " , i , disk - > name , esc ( file - > sub ) ) ;
2014-04-04 23:06:00 +02:00
if ( handle [ j ] . st . st_size ! = file - > size ) {
2015-04-20 22:07:42 +02:00
log_error ( " Unexpected size change at file '%s' from % " PRIu64 " to % " PRIu64 " . \n " , handle [ j ] . path , file - > size , handle [ j ] . st . st_size ) ;
2014-04-04 23:06:00 +02:00
} else if ( handle [ j ] . st . st_mtime ! = file - > mtime_sec
| | STAT_NSEC ( & handle [ j ] . st ) ! = file - > mtime_nsec ) {
2015-04-20 22:07:42 +02:00
log_error ( " Unexpected time change at file '%s' from % " PRIu64 " .%d to % " PRIu64 " .%d. \n " , handle [ j ] . path , file - > mtime_sec , file - > mtime_nsec , ( uint64_t ) handle [ j ] . st . st_mtime , ( uint32_t ) STAT_NSEC ( & handle [ j ] . st ) ) ;
2014-02-02 23:03:37 +01:00
} else {
2015-04-20 22:07:42 +02:00
log_error ( " Unexpected inode change from % " PRIu64 " to % " PRIu64 " at file '%s'. \n " , file - > inode , ( uint64_t ) handle [ j ] . st . st_ino , handle [ j ] . path ) ;
2014-02-02 23:03:37 +01:00
}
2015-04-20 22:07:42 +02:00
log_error ( " WARNING! You cannot modify files during a sync. \n " ) ;
log_error ( " Rerun the sync command when finished. \n " ) ;
2014-02-02 23:03:37 +01:00
+ + error ;
/* if the file is changed, it means that it was modified during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
continue ;
}
2015-10-03 10:43:02 +02:00
read_size = handle_read ( & handle [ j ] , file_pos , buffer , state - > block_size , log_fatal , 0 ) ;
2014-02-02 23:03:37 +01:00
if ( read_size = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
2015-10-03 10:43:02 +02:00
log_tag ( " error:%u:%s:%s: Read EIO error at position %u. %s \n " , i , disk - > name , esc ( file - > sub ) , file_pos , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that disk '%s' is sane and that file '%s' can be read. \n " , disk - > dir , handle [ j ] . path ) ;
log_fatal ( " Stopping at block %u \n " , i ) ;
2014-12-27 23:14:06 +01:00
+ + io_error ;
goto bail ;
}
2015-10-03 10:43:02 +02:00
log_tag ( " error:%u:%s:%s: Read error at position %u. %s \n " , i , disk - > name , esc ( file - > sub ) , file_pos , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Unexpected read error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that file '%s' can be read. \n " , handle [ j ] . path ) ;
log_fatal ( " Stopping to allow recovery. Try with 'snapraid check -f %s' \n " , file - > sub ) ;
2014-02-02 23:03:37 +01:00
+ + error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2014-02-02 23:03:37 +01:00
}
2014-06-25 22:32:36 +02:00
/* until now is disk */
2016-01-20 21:30:00 +01:00
state_usage_disk ( state , handle , & j , 1 ) ;
2014-06-25 22:32:36 +02:00
2014-02-02 23:03:37 +01:00
countsize + = read_size ;
/* now compute the hash */
if ( rehash ) {
memhash ( state - > prevhash , state - > prevhashseed , hash , buffer , read_size ) ;
} else {
memhash ( state - > hash , state - > hashseed , hash , buffer , read_size ) ;
}
2016-01-26 23:25:01 +01:00
/* until now is hash */
state_usage_hash ( state ) ;
2015-09-29 22:31:13 +02:00
if ( block_state = = BLOCK_STATE_REP ) {
/* compare the hash */
if ( memcmp ( hash , block - > hash , HASH_SIZE ) ! = 0 ) {
log_tag ( " error:%u:%s:%s: Unexpected data change \n " , i , disk - > name , esc ( file - > sub ) ) ;
2015-10-03 10:43:02 +02:00
log_error ( " Data change at file '%s' at position '%u' \n " , handle [ j ] . path , file_pos ) ;
2015-09-29 22:31:13 +02:00
log_error ( " WARNING! Unexpected data modification of a file without parity! \n " ) ;
if ( file_flag_has ( file , FILE_IS_COPY ) ) {
log_error ( " This file was detected as a copy of another file with the same name, size, \n " ) ;
log_error ( " and timestamp, but the file data isn't matching the assumed copy. \n " ) ;
log_error ( " If this is a false positive, and the files are expected to be different, \n " ) ;
log_error ( " you can 'sync' anyway using 'snapraid --force-nocopy sync' \n " ) ;
} else {
log_error ( " Try removing the file from the array and rerun the 'sync' command! \n " ) ;
}
2014-02-02 23:03:37 +01:00
2015-09-29 22:31:13 +02:00
/* block sync to allow a recovery before overwriting */
/* the parity needed to make such recovery */
* skip_sync = 1 ; /* avoid to run the next sync */
2014-02-02 23:03:37 +01:00
2016-01-10 22:04:16 +01:00
+ + silent_error ;
2015-09-29 22:31:13 +02:00
continue ;
}
} else {
/* the only other case is BLOCK_STATE_CHG */
assert ( block_state = = BLOCK_STATE_CHG ) ;
/* copy the hash in the block */
memcpy ( block - > hash , hash , HASH_SIZE ) ;
/* and mark the block as hashed */
block_state_set ( block , BLOCK_STATE_REP ) ;
/* mark the state as needing write */
state - > need_write = 1 ;
}
2014-02-02 23:03:37 +01:00
/* count the number of processed block */
+ + countpos ;
/* 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 */
2014-12-27 23:14:06 +01:00
* skip_sync = 1 ; /* avoid to run the next sync */
2014-02-02 23:03:37 +01:00
break ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2014-02-02 23:03:37 +01:00
}
}
/* close the last file in the disk */
if ( handle [ j ] . file ! = 0 ) {
2014-12-27 23:14:06 +01:00
/* keep a pointer at the file we are going to close for error reporting */
2015-10-03 10:43:02 +02:00
struct snapraid_file * report = handle [ j ] . file ;
2014-02-02 23:03:37 +01:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2014-02-02 23:03:37 +01:00
/* This one is really an unexpected error, because we are only reading */
/* and closing a descriptor should never fail */
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
2015-10-03 10:43:02 +02:00
log_tag ( " error:%u:%s:%s: Close EIO error. %s \n " , blockmax , disk - > name , esc ( report - > sub ) , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle [ j ] . path ) ;
log_fatal ( " Stopping at block %u \n " , blockmax ) ;
2014-12-27 23:14:06 +01:00
+ + io_error ;
goto bail ;
}
2015-10-03 10:43:02 +02:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , blockmax , disk - > name , esc ( report - > sub ) , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Unexpected close error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that file '%s' can be accessed. \n " , handle [ j ] . path ) ;
log_fatal ( " Stopping at block %u \n " , blockmax ) ;
2014-02-02 23:03:37 +01:00
+ + error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2014-02-02 23:03:37 +01:00
}
}
}
2014-09-27 00:23:05 +02:00
end :
2014-02-02 23:03:37 +01:00
state_progress_end ( state , countpos , countmax , countsize ) ;
2015-03-26 19:37:21 +01:00
/* note that at this point no io_error is possible */
/* becasue at the first one we bail out */
assert ( io_error = = 0 ) ;
2016-01-10 22:04:16 +01:00
if ( error | | io_error | | silent_error ) {
2015-02-22 14:31:03 +01:00
msg_status ( " \n " ) ;
msg_status ( " %8u file errors \n " , error ) ;
2016-01-10 22:04:16 +01:00
msg_status ( " %8u io errors \n " , io_error ) ;
msg_status ( " %8u data errors \n " , silent_error ) ;
2014-02-02 23:03:37 +01:00
} else {
/* print the result only if processed something */
if ( countpos ! = 0 )
2015-02-22 14:31:03 +01:00
msg_status ( " Everything OK \n " ) ;
2014-02-02 23:03:37 +01:00
}
2015-02-22 14:31:03 +01:00
if ( error )
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Unexpected file errors! \n " ) ;
2015-02-22 14:31:03 +01:00
2015-04-20 22:07:42 +02:00
log_tag ( " hash_summary:error_file:%u \n " , error ) ;
2014-02-02 23:03:37 +01:00
2014-12-27 23:14:06 +01:00
/* proceed without bailing out */
goto finish ;
2014-02-02 23:03:37 +01:00
bail :
2014-12-27 23:14:06 +01:00
/* on bail, don't run the next sync */
* skip_sync = 1 ;
/* close files left open */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
2015-02-03 18:12:52 +01:00
struct snapraid_file * file = handle [ j ] . file ;
struct snapraid_disk * disk = handle [ j ] . disk ;
2014-02-02 23:03:37 +01:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
2015-04-20 22:07:42 +02:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , i , disk - > name , esc ( file - > sub ) , strerror ( errno ) ) ;
log_fatal ( " DANGER! Unexpected close error in a data disk. \n " ) ;
2014-02-02 23:03:37 +01:00
+ + error ;
/* continue, as we are already exiting */
}
}
2014-12-27 23:14:06 +01:00
finish :
2014-02-02 23:03:37 +01:00
free ( handle ) ;
free ( buffer ) ;
2014-02-08 16:49:24 +01:00
2016-01-10 22:04:16 +01:00
if ( error + io_error + silent_error ! = 0 )
2014-12-27 23:14:06 +01:00
return - 1 ;
return 0 ;
2014-02-02 23:03:37 +01:00
}
2011-03-04 20:59:21 +01:00
/****************************************************************************/
/* sync */
2016-01-10 23:00:11 +01:00
/**
* Sync plan to use .
*/
struct snapraid_plan {
unsigned handle_max ;
struct snapraid_handle * handle_map ;
} ;
2014-01-14 22:10:47 +01:00
/**
* A block that failed the hash check , or that was deleted .
*/
struct failed_struct {
unsigned index ; /**< Index of the failed block. */
unsigned size ; /**< Size of the block. */
struct snapraid_block * block ; /**< The failed block, or BLOCK_DELETED for a deleted block */
} ;
2015-12-30 23:45:11 +01:00
/**
* Comparison function for sorting by index .
*/
int failed_compare_by_index ( const void * void_a , const void * void_b )
{
const struct failed_struct * a = void_a ;
const struct failed_struct * b = void_b ;
if ( a - > index < b - > index )
return - 1 ;
if ( a - > index > b - > index )
return 1 ;
return 0 ;
}
2013-07-01 23:08:25 +02:00
/**
* Buffer for storing the new hashes .
*/
struct snapraid_rehash {
unsigned char hash [ HASH_SIZE ] ;
struct snapraid_block * block ;
} ;
2014-12-29 19:11:03 +01:00
/**
* Check if we have to process the specified block index : : i .
*/
2015-12-30 23:45:11 +01:00
static int block_is_enabled ( void * void_plan , block_off_t i )
2014-12-29 19:11:03 +01:00
{
2015-12-30 23:45:11 +01:00
struct snapraid_plan * plan = void_plan ;
2014-12-29 19:11:03 +01:00
unsigned j ;
int one_invalid ;
int one_valid ;
/* for each disk */
one_invalid = 0 ;
one_valid = 0 ;
2015-12-30 23:45:11 +01:00
for ( j = 0 ; j < plan - > handle_max ; + + j ) {
2014-12-29 19:11:03 +01:00
struct snapraid_block * block ;
2015-12-30 23:45:11 +01:00
struct snapraid_disk * disk = plan - > handle_map [ j ] . disk ;
2014-12-29 19:11:03 +01:00
/* if no disk, nothing to check */
2015-12-30 23:45:11 +01:00
if ( ! disk )
2014-12-29 19:11:03 +01:00
continue ;
2016-01-16 14:26:05 +01:00
block = fs_par2block_find ( disk , i ) ;
2014-12-29 19:11:03 +01:00
if ( block_has_file ( block ) )
one_valid = 1 ;
if ( block_has_invalid_parity ( block ) )
one_invalid = 1 ;
}
/* if none valid or none invalid, we don't need to update */
if ( ! one_invalid | | ! one_valid )
return 0 ;
return 1 ;
}
2015-12-30 23:45:11 +01:00
static void sync_data_reader ( struct snapraid_worker * worker , struct snapraid_task * task )
{
struct snapraid_io * io = worker - > io ;
struct snapraid_state * state = io - > state ;
struct snapraid_handle * handle = worker - > handle ;
struct snapraid_disk * disk = handle - > disk ;
block_off_t blockcur = task - > position ;
unsigned char * buffer = task - > buffer ;
int ret ;
/* if the disk position is not used */
if ( ! disk ) {
/* use an empty block */
memset ( buffer , 0 , state - > block_size ) ;
task - > state = TASK_STATE_DONE ;
return ;
}
/* get the block */
2016-01-16 14:26:05 +01:00
task - > block = fs_par2block_find ( disk , blockcur ) ;
2015-12-30 23:45:11 +01:00
/* if the block has no file, meanining that it's EMPTY or DELETED, */
/* it doesn't partecipate in the new parity computation */
if ( ! block_has_file ( task - > block ) ) {
/* use an empty block */
memset ( buffer , 0 , state - > block_size ) ;
task - > state = TASK_STATE_DONE ;
return ;
}
/* get the file of this block */
2016-01-01 10:00:18 +01:00
task - > file = fs_par2file_get ( disk , blockcur , & task - > file_pos ) ;
2015-12-30 23:45:11 +01:00
/* if the file is different than the current one, close it */
if ( handle - > file ! = 0 & & handle - > file ! = task - > file ) {
/* keep a pointer at the file we are going to close for error reporting */
struct snapraid_file * report = handle - > file ;
ret = handle_close ( handle ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
/* This one is really an unexpected error, because we are only reading */
/* and closing a descriptor should never fail */
if ( errno = = EIO ) {
log_tag ( " error:%u:%s:%s: Close EIO error. %s \n " , blockcur , disk - > name , esc ( report - > sub ) , strerror ( errno ) ) ;
log_fatal ( " DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle - > path ) ;
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
task - > state = TASK_STATE_IOERROR ;
return ;
}
log_tag ( " error:%u:%s:%s: Close error. %s \n " , blockcur , disk - > name , esc ( report - > sub ) , strerror ( errno ) ) ;
log_fatal ( " WARNING! Unexpected close error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that file '%s' can be accessed. \n " , handle - > path ) ;
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
task - > state = TASK_STATE_ERROR ;
return ;
/* LCOV_EXCL_STOP */
}
}
ret = handle_open ( handle , task - > file , state - > file_mode , log_error , 0 ) ;
if ( ret = = - 1 ) {
if ( errno = = EIO ) {
/* LCOV_EXCL_START */
log_tag ( " error:%u:%s:%s: Open EIO error. %s \n " , blockcur , disk - > name , esc ( task - > file - > sub ) , strerror ( errno ) ) ;
log_fatal ( " DANGER! Unexpected input/output open error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle - > path ) ;
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
task - > state = TASK_STATE_IOERROR ;
return ;
/* LCOV_EXCL_STOP */
}
if ( errno = = ENOENT ) {
log_tag ( " error:%u:%s:%s: Open ENOENT error. %s \n " , blockcur , disk - > name , esc ( task - > file - > sub ) , strerror ( errno ) ) ;
log_error ( " Missing file '%s'. \n " , handle - > path ) ;
log_error ( " WARNING! You cannot modify data disk during a sync. \n " ) ;
log_error ( " Rerun the sync command when finished. \n " ) ;
/* if the file is missing, it means that it was removed during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
task - > state = TASK_STATE_ERROR_CONTINUE ;
return ;
}
if ( errno = = EACCES ) {
log_tag ( " error:%u:%s:%s: Open EACCES error. %s \n " , blockcur , disk - > name , esc ( task - > file - > sub ) , strerror ( errno ) ) ;
log_error ( " No access at file '%s'. \n " , handle - > path ) ;
log_error ( " WARNING! Please fix the access permission in the data disk. \n " ) ;
log_error ( " Rerun the sync command when finished. \n " ) ;
/* this isn't a serious error, so we skip this block, and continue with others */
task - > state = TASK_STATE_ERROR_CONTINUE ;
return ;
}
/* LCOV_EXCL_START */
log_tag ( " error:%u:%s:%s: Open error. %s \n " , blockcur , disk - > name , esc ( task - > file - > sub ) , strerror ( errno ) ) ;
log_fatal ( " WARNING! Unexpected open error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that file '%s' can be accessed. \n " , handle - > path ) ;
log_fatal ( " Stopping to allow recovery. Try with 'snapraid check -f %s' \n " , task - > file - > sub ) ;
task - > state = TASK_STATE_ERROR ;
return ;
/* LCOV_EXCL_STOP */
}
/* check if the file is changed */
if ( handle - > st . st_size ! = task - > file - > size
| | handle - > st . st_mtime ! = task - > file - > mtime_sec
| | STAT_NSEC ( & handle - > st ) ! = task - > file - > mtime_nsec
| | handle - > st . st_ino ! = task - > file - > inode
) {
log_tag ( " error:%u:%s:%s: Unexpected attribute change \n " , blockcur , disk - > name , esc ( task - > file - > sub ) ) ;
if ( handle - > st . st_size ! = task - > file - > size ) {
log_error ( " Unexpected size change at file '%s' from % " PRIu64 " to % " PRIu64 " . \n " , handle - > path , task - > file - > size , handle - > st . st_size ) ;
} else if ( handle - > st . st_mtime ! = task - > file - > mtime_sec
| | STAT_NSEC ( & handle - > st ) ! = task - > file - > mtime_nsec ) {
log_error ( " Unexpected time change at file '%s' from % " PRIu64 " .%d to % " PRIu64 " .%d. \n " , handle - > path , task - > file - > mtime_sec , task - > file - > mtime_nsec , ( uint64_t ) handle - > st . st_mtime , ( uint32_t ) STAT_NSEC ( & handle - > st ) ) ;
} else {
log_error ( " Unexpected inode change from % " PRIu64 " to % " PRIu64 " at file '%s'. \n " , task - > file - > inode , ( uint64_t ) handle - > st . st_ino , handle - > path ) ;
}
log_error ( " WARNING! You cannot modify files during a sync. \n " ) ;
log_error ( " Rerun the sync command when finished. \n " ) ;
/* if the file is changed, it means that it was modified during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
task - > state = TASK_STATE_ERROR_CONTINUE ;
return ;
}
task - > read_size = handle_read ( handle , task - > file_pos , buffer , state - > block_size , log_error , 0 ) ;
if ( task - > read_size = = - 1 ) {
/* LCOV_EXCL_START */
if ( errno = = EIO ) {
log_tag ( " error:%u:%s:%s: Read EIO error at position %u. %s \n " , blockcur , disk - > name , esc ( task - > file - > sub ) , task - > file_pos , strerror ( errno ) ) ;
log_error ( " Input/Output error in file '%s' at position '%u' \n " , handle - > path , task - > file_pos ) ;
task - > state = TASK_STATE_IOERROR_CONTINUE ;
return ;
}
log_tag ( " error:%u:%s:%s: Read error at position %u. %s \n " , blockcur , disk - > name , esc ( task - > file - > sub ) , task - > file_pos , strerror ( errno ) ) ;
log_fatal ( " WARNING! Unexpected read error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that file '%s' can be read. \n " , handle - > path ) ;
log_fatal ( " Stopping to allow recovery. Try with 'snapraid check -f %s' \n " , task - > file - > sub ) ;
task - > state = TASK_STATE_ERROR ;
return ;
/* LCOV_EXCL_STOP */
}
/* store the path of the opened file */
pathcpy ( task - > path , sizeof ( task - > path ) , handle - > path ) ;
task - > state = TASK_STATE_DONE ;
}
2016-01-04 11:28:34 +01:00
static void sync_parity_writer ( struct snapraid_worker * worker , struct snapraid_task * task )
{
struct snapraid_io * io = worker - > io ;
struct snapraid_state * state = io - > state ;
struct snapraid_parity_handle * parity_handle = worker - > parity_handle ;
unsigned level = parity_handle - > level ;
block_off_t blockcur = task - > position ;
unsigned char * buffer = task - > buffer ;
int ret ;
/* write parity */
ret = parity_write ( parity_handle , blockcur , buffer , state - > block_size ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
if ( errno = = EIO ) {
log_tag ( " parity_error:%u:%s: Write EIO error. %s \n " , blockcur , lev_config_name ( level ) , strerror ( errno ) ) ;
log_error ( " Input/Output error in parity '%s' at position '%u' \n " , lev_config_name ( level ) , blockcur ) ;
task - > state = TASK_STATE_IOERROR_CONTINUE ;
return ;
}
log_tag ( " parity_error:%u:%s: Write error. %s \n " , blockcur , lev_config_name ( level ) , strerror ( errno ) ) ;
log_fatal ( " WARNING! Unexpected write error in the %s disk, it isn't possible to sync. \n " , lev_name ( level ) ) ;
log_fatal ( " Ensure that disk '%s' has some free space available. \n " , lev_config_name ( level ) ) ;
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
task - > state = TASK_STATE_ERROR ;
return ;
/* LCOV_EXCL_STOP */
}
task - > state = TASK_STATE_DONE ;
}
2016-01-04 11:32:02 +01:00
static int state_sync_process ( struct snapraid_state * state , struct snapraid_parity_handle * parity_handle , block_off_t blockstart , block_off_t blockmax )
2011-03-04 20:59:21 +01:00
{
2015-12-30 23:45:11 +01:00
struct snapraid_io io ;
struct snapraid_plan plan ;
2011-03-04 20:59:21 +01:00
struct snapraid_handle * handle ;
2013-07-01 23:08:25 +02:00
void * rehandle_alloc ;
struct snapraid_rehash * rehandle ;
2011-09-23 21:48:50 +02:00
unsigned diskmax ;
2015-12-30 23:45:11 +01:00
block_off_t blockcur ;
2011-03-04 20:59:21 +01:00
unsigned j ;
2015-12-30 23:45:11 +01:00
void * zero_alloc ;
void * * zero ;
2016-01-05 14:48:39 +01:00
void * copy_alloc ;
void * * copy ;
2011-04-23 21:38:26 +02:00
unsigned buffermax ;
2011-03-28 20:55:57 +02:00
data_off_t countsize ;
block_off_t countpos ;
block_off_t countmax ;
2012-07-21 13:52:22 +02:00
block_off_t autosavedone ;
block_off_t autosavelimit ;
block_off_t autosavemissing ;
2011-03-18 22:38:11 +01:00
int ret ;
2013-09-21 19:11:11 +02:00
unsigned error ;
unsigned silent_error ;
2014-12-27 23:14:06 +01:00
unsigned io_error ;
2013-06-23 22:54:54 +02:00
time_t now ;
2014-01-14 22:10:47 +01:00
struct failed_struct * failed ;
int * failed_map ;
2013-09-15 21:12:41 +02:00
unsigned l ;
2016-01-20 21:30:00 +01:00
unsigned * waiting_map ;
unsigned waiting_mac ;
2013-06-23 22:54:54 +02:00
2013-09-29 21:01:31 +02:00
/* the sync process assumes that all the hashes are correct */
/* including the ones from CHG and DELETED blocks */
2014-06-13 20:52:03 +02:00
assert ( state - > clear_past_hash ! = 0 ) ;
2013-09-29 21:01:31 +02:00
2013-06-23 22:54:54 +02:00
/* get the present time */
now = time ( 0 ) ;
2011-03-04 20:59:21 +01:00
2011-09-23 21:48:50 +02:00
/* maps the disks to handles */
2016-01-18 22:38:01 +01:00
handle = handle_mapping ( state , & diskmax ) ;
2011-09-23 21:48:50 +02:00
2013-07-01 23:08:25 +02:00
/* rehash buffers */
rehandle = malloc_nofail_align ( diskmax * sizeof ( struct snapraid_rehash ) , & rehandle_alloc ) ;
2016-01-05 14:48:39 +01:00
/* we need 1 * data + 1 * parity */
buffermax = diskmax + state - > level ;
2011-04-23 21:38:26 +02:00
2015-12-30 23:45:11 +01:00
/* initialize the io threads */
2016-01-10 19:39:43 +01:00
io_init ( & io , state , state - > opt . io_cache , buffermax , sync_data_reader , handle , diskmax , 0 , sync_parity_writer , parity_handle , state - > level ) ;
2011-03-04 20:59:21 +01:00
2016-01-05 14:48:39 +01:00
/* allocate the copy buffer */
copy = malloc_nofail_vector_align ( diskmax , diskmax , state - > block_size , & copy_alloc ) ;
/* allocate and fill the zero buffer */
2015-12-30 23:45:11 +01:00
zero = malloc_nofail_align ( state - > block_size , & zero_alloc ) ;
memset ( zero , 0 , state - > block_size ) ;
raid_zero ( zero ) ;
2014-01-14 22:10:47 +01:00
failed = malloc_nofail ( diskmax * sizeof ( struct failed_struct ) ) ;
failed_map = malloc_nofail ( diskmax * sizeof ( unsigned ) ) ;
2016-01-21 18:52:45 +01:00
/* possibly waiting disks */
waiting_mac = diskmax > RAID_PARITY_MAX ? diskmax : RAID_PARITY_MAX ;
waiting_map = malloc_nofail ( waiting_mac * sizeof ( unsigned ) ) ;
2014-01-14 22:10:47 +01:00
2013-09-21 19:11:11 +02:00
error = 0 ;
silent_error = 0 ;
2014-12-27 23:14:06 +01:00
io_error = 0 ;
2011-03-04 20:59:21 +01:00
2011-03-28 20:55:57 +02:00
/* first count the number of blocks to process */
countmax = 0 ;
2015-12-30 23:45:11 +01:00
plan . handle_max = diskmax ;
plan . handle_map = handle ;
for ( blockcur = blockstart ; blockcur < blockmax ; + + blockcur ) {
if ( ! block_is_enabled ( & plan , blockcur ) )
2011-03-28 20:55:57 +02:00
continue ;
+ + countmax ;
}
2012-07-21 13:52:22 +02:00
/* compute the autosave size for all disk, even if not read */
/* this makes sense because the speed should be almost the same */
/* if the disks are read in parallel */
autosavelimit = state - > autosave / ( diskmax * state - > block_size ) ;
autosavemissing = countmax ; /* blocks to do */
autosavedone = 0 ; /* blocks done */
2014-06-25 22:32:36 +02:00
/* drop until now */
state_usage_waste ( state ) ;
2011-03-28 20:55:57 +02:00
countsize = 0 ;
countpos = 0 ;
2015-12-30 23:45:11 +01:00
/* start all the worker threads */
io_start ( & io , blockstart , blockmax , & block_is_enabled , & plan ) ;
2014-09-27 00:23:05 +02:00
if ( ! state_progress_begin ( state , blockstart , blockmax , countmax ) )
goto end ;
2015-12-30 23:45:11 +01:00
while ( 1 ) {
2014-01-14 22:10:47 +01:00
unsigned failed_count ;
2013-10-03 22:16:29 +02:00
int error_on_this_block ;
int silent_error_on_this_block ;
2014-12-30 09:45:35 +01:00
int io_error_on_this_block ;
2014-01-14 22:10:47 +01:00
int fixed_error_on_this_block ;
2013-09-29 21:01:31 +02:00
int parity_needs_to_be_updated ;
2016-01-04 11:28:34 +01:00
int parity_going_to_be_updated ;
2013-07-01 23:08:25 +02:00
snapraid_info info ;
int rehash ;
2015-12-30 23:45:11 +01:00
void * * buffer ;
2016-01-05 13:20:25 +01:00
int writer_error [ IO_WRITER_ERROR_MAX ] ;
2011-03-04 20:59:21 +01:00
2015-12-30 23:45:11 +01:00
/* go to the next block */
2016-01-04 11:24:04 +01:00
blockcur = io_read_next ( & io , & buffer ) ;
2015-12-30 23:45:11 +01:00
if ( blockcur > = blockmax )
break ;
2011-03-04 20:59:21 +01:00
2016-01-31 16:38:29 +01:00
/* until now is scheduling */
state_usage_sched ( state ) ;
2012-07-21 13:52:22 +02:00
/* one more block processed for autosave */
+ + autosavedone ;
- - autosavemissing ;
2015-12-30 23:45:11 +01:00
/* by default process the block, and skip it if something goes wrong */
2013-10-03 22:16:29 +02:00
error_on_this_block = 0 ;
silent_error_on_this_block = 0 ;
2014-12-30 09:45:35 +01:00
io_error_on_this_block = 0 ;
2014-01-14 22:10:47 +01:00
fixed_error_on_this_block = 0 ;
/* keep track of the number of failed blocks */
failed_count = 0 ;
2011-09-14 22:52:55 +02:00
2013-10-25 18:03:05 +02:00
/* get block specific info */
2015-12-30 23:45:11 +01:00
info = info_get ( & state - > infoarr , blockcur ) ;
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 ) ;
2016-01-04 11:28:34 +01:00
/* if the parity requires to be updated */
/* It could happens that all the blocks are EMPTY/BLK and CHG but with the hash */
2013-09-29 21:01:31 +02:00
/* still matching because the specific CHG block was not modified. */
2016-01-04 11:28:34 +01:00
/* In such case, we can avoid to update parity, because it would be the same as before */
2013-11-01 08:38:13 +01:00
/* Note that CHG/DELETED blocks already present in the content file loaded */
2014-10-19 22:25:30 +02:00
/* have the hash cleared (::clear_past_hash flag), and then they won't never match the hash. */
2013-11-01 08:38:13 +01:00
/* We are treating only CHG blocks created at runtime. */
2016-01-04 10:40:41 +01:00
parity_needs_to_be_updated = state - > opt . force_parity_update ;
2013-09-29 21:01:31 +02:00
2016-01-04 11:28:34 +01:00
/* if the parity is going to be updated */
parity_going_to_be_updated = 0 ;
2013-09-29 21:01:31 +02:00
/* if the block is marked as bad, we force the parity update */
/* because the bad block may be the result of a wrong parity */
if ( info_get_bad ( info ) )
parity_needs_to_be_updated = 1 ;
2011-03-04 20:59:21 +01:00
/* for each disk, process the block */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
2015-12-30 23:45:11 +01:00
struct snapraid_task * task ;
2011-03-04 20:59:21 +01:00
int read_size ;
2011-04-12 22:16:35 +02:00
unsigned char hash [ HASH_SIZE ] ;
2011-03-04 20:59:21 +01:00
struct snapraid_block * block ;
2014-02-01 17:57:26 +01:00
unsigned block_state ;
2015-12-30 23:45:11 +01:00
struct snapraid_disk * disk ;
2015-10-03 10:43:02 +02:00
struct snapraid_file * file ;
block_off_t file_pos ;
2015-12-30 23:45:11 +01:00
unsigned diskcur ;
2016-01-26 23:25:01 +01:00
/* until now is misc */
state_usage_misc ( state ) ;
2015-12-30 23:45:11 +01:00
2016-01-20 21:30:00 +01:00
task = io_data_read ( & io , & diskcur , waiting_map , & waiting_mac ) ;
/* until now is disk */
state_usage_disk ( state , handle , waiting_map , waiting_mac ) ;
2015-12-30 23:45:11 +01:00
/* get the results */
disk = task - > disk ;
block = task - > block ;
file = task - > file ;
file_pos = task - > file_pos ;
read_size = task - > read_size ;
2011-03-04 20:59:21 +01:00
2013-07-01 23:08:25 +02:00
/* by default no rehash in case of "continue" */
2015-12-30 23:45:11 +01:00
rehandle [ diskcur ] . block = 0 ;
2013-07-01 23:08:25 +02:00
2011-10-01 19:18:32 +02:00
/* if the disk position is not used */
2015-12-30 23:45:11 +01:00
if ( ! disk )
2011-10-01 19:18:32 +02:00
continue ;
2014-02-01 17:57:26 +01:00
/* get the state of the block */
block_state = block_state_get ( block ) ;
2013-09-29 21:01:31 +02:00
2014-02-01 17:57:26 +01:00
/* if the block has invalid parity, */
2014-01-14 22:10:47 +01:00
/* we have to take care of it in case of recover */
if ( block_has_invalid_parity ( block ) ) {
/* store it in the failed set, because */
2014-02-01 17:57:26 +01:00
/* the parity may be still computed with the previous content */
2015-12-30 23:45:11 +01:00
failed [ failed_count ] . index = diskcur ;
2014-01-14 22:10:47 +01:00
failed [ failed_count ] . size = state - > block_size ;
failed [ failed_count ] . block = block ;
+ + failed_count ;
2014-02-01 17:57:26 +01:00
/* if the block has invalid parity, we have to update the parity */
/* to include this block change */
/* This also apply to CHG blocks, but we are going to handle */
/* later this case to do the updates only if really needed */
if ( block_state ! = BLOCK_STATE_CHG )
parity_needs_to_be_updated = 1 ;
/* note that DELETE blocks are skipped in the next check */
/* and we have to store them in the failed blocks */
/* before skipping */
/* follow */
2013-09-29 21:01:31 +02:00
}
2015-12-30 23:45:11 +01:00
/* if the block is not used */
if ( ! block_has_file ( block ) )
2011-03-04 20:59:21 +01:00
continue ;
2014-12-27 23:14:06 +01:00
2015-12-30 23:45:11 +01:00
/* handle error conditions */
if ( task - > state = = TASK_STATE_IOERROR ) {
+ + io_error ;
goto bail ;
2011-03-18 22:38:11 +01:00
}
2015-12-30 23:45:11 +01:00
if ( task - > state = = TASK_STATE_ERROR ) {
2013-09-21 19:11:11 +02:00
+ + error ;
2011-03-19 22:00:08 +01:00
goto bail ;
}
2015-12-30 23:45:11 +01:00
if ( task - > state = = TASK_STATE_ERROR_CONTINUE ) {
2013-09-21 19:11:11 +02:00
+ + error ;
2013-10-03 22:16:29 +02:00
error_on_this_block = 1 ;
2011-09-14 22:52:55 +02:00
continue ;
2011-03-18 22:38:11 +01:00
}
2015-12-30 23:45:11 +01:00
if ( task - > state = = TASK_STATE_IOERROR_CONTINUE ) {
+ + io_error ;
2016-01-02 17:13:19 +01:00
if ( io_error > = state - > opt . io_error_limit ) {
log_fatal ( " DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Ensure that disk '%s' is sane and that file '%s' can be read. \n " , disk - > dir , task - > path ) ;
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
goto bail ;
}
/* otherwise continue */
2015-12-30 23:45:11 +01:00
io_error_on_this_block = 1 ;
continue ;
}
if ( task - > state ! = TASK_STATE_DONE ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-12-30 23:45:11 +01:00
log_fatal ( " Internal inconsistency in task state \n " ) ;
os_abort ( ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-03-18 22:38:11 +01:00
}
2011-03-04 20:59:21 +01:00
2012-02-09 23:17:36 +01:00
countsize + = read_size ;
2011-03-04 20:59:21 +01:00
/* now compute the hash */
2013-07-01 23:08:25 +02:00
if ( rehash ) {
2015-12-30 23:45:11 +01:00
memhash ( state - > prevhash , state - > prevhashseed , hash , buffer [ diskcur ] , read_size ) ;
2013-07-01 23:08:25 +02:00
/* compute the new hash, and store it */
2015-12-30 23:45:11 +01:00
rehandle [ diskcur ] . block = block ;
memhash ( state - > hash , state - > hashseed , rehandle [ diskcur ] . hash , buffer [ diskcur ] , read_size ) ;
2013-07-01 23:08:25 +02:00
} else {
2015-12-30 23:45:11 +01:00
memhash ( state - > hash , state - > hashseed , hash , buffer [ diskcur ] , read_size ) ;
2013-07-01 23:08:25 +02:00
}
2011-03-04 20:59:21 +01:00
2016-01-26 23:25:01 +01:00
/* until now is hash */
state_usage_hash ( state ) ;
2013-08-21 22:24:12 +02:00
if ( block_has_updated_hash ( block ) ) {
2011-03-04 20:59:21 +01:00
/* compare the hash */
2011-04-12 22:16:35 +02:00
if ( memcmp ( hash , block - > hash , HASH_SIZE ) ! = 0 ) {
2014-02-01 17:57:26 +01:00
/* if the file has invalid parity, it's a REP changed during the sync */
if ( block_has_invalid_parity ( block ) ) {
2015-12-30 23:45:11 +01:00
log_tag ( " error:%u:%s:%s: Unexpected data change \n " , blockcur , disk - > name , esc ( file - > sub ) ) ;
log_error ( " Data change at file '%s' at position '%u' \n " , task - > path , file_pos ) ;
2015-04-20 22:07:42 +02:00
log_error ( " WARNING! Unexpected data modification of a file without parity! \n " ) ;
2014-05-15 23:45:51 +02:00
if ( file_flag_has ( file , FILE_IS_COPY ) ) {
2015-04-20 22:07:42 +02:00
log_error ( " This file was detected as a copy of another file with the same name, size, \n " ) ;
log_error ( " and timestamp, but the file data isn't matching the assumed copy. \n " ) ;
log_error ( " If this is a false positive, and the files are expected to be different, \n " ) ;
log_error ( " you can 'sync' anyway using 'snapraid --force-nocopy sync' \n " ) ;
2014-05-15 23:45:51 +02:00
} else {
2015-04-20 22:07:42 +02:00
log_error ( " Try removing the file from the array and rerun the 'sync' command! \n " ) ;
2014-05-15 23:45:51 +02:00
}
2014-02-01 17:57:26 +01:00
+ + error ;
/* if the file is changed, it means that it was modified during sync */
/* this isn't a serious error, so we skip this block, and continue with others */
error_on_this_block = 1 ;
continue ;
} else { /* otherwise it's a BLK with silent error */
2015-09-12 15:19:52 +02:00
unsigned diff = memdiff ( hash , block - > hash , HASH_SIZE ) ;
2015-12-30 23:45:11 +01:00
log_tag ( " error:%u:%s:%s: Data error at position %u, diff bits %u \n " , blockcur , disk - > name , esc ( file - > sub ) , file_pos , diff ) ;
log_error ( " Data error in file '%s' at position '%u', diff bits %u \n " , task - > path , file_pos , diff ) ;
2014-02-01 17:57:26 +01:00
/* save the failed block for the fix */
2015-12-30 23:45:11 +01:00
failed [ failed_count ] . index = diskcur ;
2014-02-01 17:57:26 +01:00
failed [ failed_count ] . size = read_size ;
failed [ failed_count ] . block = block ;
+ + failed_count ;
/* silent errors are very rare, and are not a signal that a disk */
/* is going to fail. So, we just continue marking the block as bad */
/* just like in scrub */
+ + silent_error ;
silent_error_on_this_block = 1 ;
continue ;
}
2011-03-04 20:59:21 +01:00
}
} else {
2014-02-01 17:57:26 +01:00
/* if until now the parity doesn't need to be updated */
2013-09-29 21:01:31 +02:00
if ( ! parity_needs_to_be_updated ) {
/* for sure it's a CHG block, because EMPTY are processed before with "continue" */
2014-02-01 17:57:26 +01:00
/* and BLK and REP have "block_has_updated_hash()" as 1, and all the others */
2013-09-29 21:01:31 +02:00
/* have "parity_needs_to_be_updated" already at 1 */
assert ( block_state_get ( block ) = = BLOCK_STATE_CHG ) ;
/* if there is an hash */
2014-02-01 17:57:26 +01:00
if ( hash_is_real ( block - > hash ) ) {
2013-09-29 21:01:31 +02:00
/* check if the hash is changed */
if ( memcmp ( hash , block - > hash , HASH_SIZE ) ! = 0 ) {
/* the block is different, and we must update parity */
parity_needs_to_be_updated = 1 ;
}
} else {
/* if the hash is already invalid, we update parity */
parity_needs_to_be_updated = 1 ;
}
}
2014-09-27 00:32:39 +02:00
2013-07-01 23:08:25 +02:00
/* copy the hash in the block, but doesn't mark the block as hashed */
/* this allow in case of skipped block to do not save the failed computation */
2011-04-12 22:16:35 +02:00
memcpy ( block - > hash , hash , HASH_SIZE ) ;
2013-07-01 23:08:25 +02:00
/* note that in case of rehash, this is the wrong hash, */
/* but it will be overwritten later */
2011-03-04 20:59:21 +01:00
}
}
2014-01-14 22:10:47 +01:00
/* if we have only silent errors we can try to fix them on-the-fly */
2014-07-01 22:56:18 +02:00
/* note the the fix is not written to disk, but used only to */
/* compute the new parity */
2014-12-30 09:45:35 +01:00
if ( ! error_on_this_block & & ! io_error_on_this_block & & silent_error_on_this_block ) {
2014-01-14 22:10:47 +01:00
unsigned failed_mac ;
2014-02-01 17:57:26 +01:00
int something_to_recover = 0 ;
2011-04-23 21:38:26 +02:00
2015-12-30 23:45:11 +01:00
/* sort the failed vector */
/* because with threads it may be in any order */
/* but RAID requires the indexes to be sorted */
qsort ( failed , failed_count , sizeof ( failed [ 0 ] ) , failed_compare_by_index ) ;
2014-01-14 22:10:47 +01:00
/* setup the blocks to recover */
failed_mac = 0 ;
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < failed_count ; + + j ) {
2014-01-14 22:10:47 +01:00
unsigned char * block_buffer = buffer [ failed [ j ] . index ] ;
2016-01-05 14:48:39 +01:00
unsigned char * block_copy = copy [ failed [ j ] . index ] ;
2014-01-14 22:10:47 +01:00
unsigned block_state = block_state_get ( failed [ j ] . block ) ;
2014-02-01 17:57:26 +01:00
/* we try to recover only if at least one BLK is present */
if ( block_state = = BLOCK_STATE_BLK )
something_to_recover = 1 ;
/* save a copy of the content just read */
/* that it's going to be overwritten by the recovering function */
2014-01-14 22:10:47 +01:00
memcpy ( block_copy , block_buffer , state - > block_size ) ;
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 filled with 0, restore this state */
/* and avoid to recover it */
2014-01-14 22:10:47 +01:00
memset ( block_buffer , 0 , state - > block_size ) ;
} else {
2014-02-01 17:57:26 +01:00
/* if we have too many failures, we cannot recover */
if ( failed_mac > = state - > level )
2014-01-14 22:10:47 +01:00
break ;
2014-02-01 17:57:26 +01:00
2014-01-14 22:10:47 +01:00
/* otherwise it has to be recovered */
failed_map [ failed_mac + + ] = failed [ j ] . index ;
}
}
2014-02-01 17:57:26 +01:00
/* if we have something to recover and enough parity */
if ( something_to_recover & & j = = failed_count ) {
2016-01-26 23:25:01 +01:00
/* until now is misc */
state_usage_misc ( state ) ;
2014-06-25 22:32:36 +02:00
2014-01-14 22:10:47 +01:00
/* read the parity */
2014-02-01 17:57:26 +01:00
/* we are sure that parity exists because */
/* we have at least one BLK block */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2016-01-04 11:32:02 +01:00
ret = parity_read ( & parity_handle [ l ] , blockcur , buffer [ diskmax + l ] , state - > block_size , log_error ) ;
2014-01-14 22:10:47 +01:00
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
2015-12-30 23:45:11 +01:00
log_tag ( " parity_error:%u:%s: Read EIO error. %s \n " , blockcur , lev_config_name ( l ) , strerror ( errno ) ) ;
2014-12-30 09:45:35 +01:00
if ( io_error > = state - > opt . io_error_limit ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected input/output read error in the %s disk, it isn't possible to sync. \n " , lev_name ( l ) ) ;
log_fatal ( " Ensure that disk '%s' is sane and can be read. \n " , lev_config_name ( l ) ) ;
2015-12-30 23:45:11 +01:00
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
2014-12-30 09:45:35 +01:00
+ + io_error ;
goto bail ;
}
2015-12-30 23:45:11 +01:00
log_error ( " Input/Output error in parity '%s' at position '%u' \n " , lev_config_name ( l ) , blockcur ) ;
2014-12-27 23:14:06 +01:00
+ + io_error ;
2014-12-30 09:45:35 +01:00
io_error_on_this_block = 1 ;
continue ;
2014-12-27 23:14:06 +01:00
}
2015-12-30 23:45:11 +01:00
log_tag ( " parity_error:%u:%s: Read error. %s \n " , blockcur , lev_config_name ( l ) , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Unexpected read error in the %s disk, it isn't possible to sync. \n " , lev_name ( l ) ) ;
log_fatal ( " Ensure that disk '%s' can be read. \n " , lev_config_name ( l ) ) ;
2015-12-30 23:45:11 +01:00
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
2014-01-14 22:10:47 +01:00
+ + error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2014-01-14 22:10:47 +01:00
}
2014-06-25 22:32:36 +02:00
/* until now is parity */
2016-01-20 21:30:00 +01:00
state_usage_parity ( state , & l , 1 ) ;
2014-01-14 22:10:47 +01:00
}
2014-12-30 09:45:35 +01:00
/* if no error in parity read */
if ( ! io_error_on_this_block ) {
/* try to fix the data */
/* note that this is a simple fix algorithm, that doesn't take into */
/* account the case of a wrong parity */
/* only 'fix' supports the most advanced fixing */
raid_rec ( failed_mac , failed_map , diskmax , state - > level , state - > block_size , buffer ) ;
2016-01-26 23:25:01 +01:00
/* until now is raid */
state_usage_raid ( state ) ;
2014-12-30 09:45:35 +01:00
/* check the result and prepare the data */
for ( j = 0 ; j < failed_count ; + + j ) {
unsigned char hash [ HASH_SIZE ] ;
unsigned char * block_buffer = buffer [ failed [ j ] . index ] ;
2016-01-05 14:48:39 +01:00
unsigned char * block_copy = copy [ failed [ j ] . index ] ;
2014-12-30 09:45:35 +01:00
unsigned block_state = block_state_get ( failed [ j ] . block ) ;
if ( block_state = = BLOCK_STATE_BLK ) {
unsigned size = failed [ j ] . size ;
/* compute the hash of the recovered block */
if ( rehash ) {
memhash ( state - > prevhash , state - > prevhashseed , hash , block_buffer , size ) ;
} else {
memhash ( state - > hash , state - > hashseed , hash , block_buffer , size ) ;
}
2016-01-26 23:25:01 +01:00
/* until now is hash */
state_usage_hash ( state ) ;
2014-12-30 09:45:35 +01:00
/* if the hash doesn't match */
if ( memcmp ( hash , failed [ j ] . block - > hash , HASH_SIZE ) ! = 0 ) {
/* we have not recovered */
break ;
}
/* pad with 0 if needed */
if ( size < state - > block_size )
memset ( block_buffer + size , 0 , state - > block_size - size ) ;
2014-01-14 22:10:47 +01:00
} else {
2014-12-30 09:45:35 +01:00
/* otherwise restore the content */
/* because we are not interested in the old state */
/* that it's recovered for CHG, REP and DELETED blocks */
memcpy ( block_buffer , block_copy , state - > block_size ) ;
2014-01-14 22:10:47 +01:00
}
}
2014-12-30 09:45:35 +01:00
/* if all is processed, we have fixed it */
if ( j = = failed_count )
fixed_error_on_this_block = 1 ;
}
2014-01-14 22:10:47 +01:00
}
}
/* if we have read all the data required and it's correct, proceed with the parity */
2014-12-30 09:45:35 +01:00
if ( ! error_on_this_block & & ! io_error_on_this_block
2014-01-14 22:10:47 +01:00
& & ( ! silent_error_on_this_block | | fixed_error_on_this_block )
) {
2015-10-07 22:07:20 +02:00
/* update the parity only if really needed */
2013-09-29 21:01:31 +02:00
if ( parity_needs_to_be_updated ) {
/* compute the parity */
2014-01-08 17:07:13 +01:00
raid_gen ( diskmax , state - > level , state - > block_size , buffer ) ;
2013-09-29 21:01:31 +02:00
2016-01-26 23:25:01 +01:00
/* until now is raid */
state_usage_raid ( state ) ;
2016-01-04 11:28:34 +01:00
/* mark that the parity is going to be written */
parity_going_to_be_updated = 1 ;
2013-09-11 23:30:32 +02:00
}
2016-01-04 11:28:34 +01:00
/* for each disk, mark the blocks as processed */
for ( j = 0 ; j < diskmax ; + + j ) {
struct snapraid_block * block ;
2015-10-05 21:40:44 +02:00
2016-01-04 11:28:34 +01:00
if ( ! handle [ j ] . disk )
continue ;
2012-02-09 23:17:36 +01:00
2016-01-16 14:26:05 +01:00
block = fs_par2block_find ( handle [ j ] . disk , blockcur ) ;
2012-02-09 23:17:36 +01:00
2016-01-16 14:26:05 +01:00
if ( block = = BLOCK_NULL ) {
2016-01-04 11:28:34 +01:00
/* nothing to do */
continue ;
}
2011-03-18 22:38:11 +01:00
2016-01-04 11:28:34 +01:00
/* if it's a deleted block */
if ( block_state_get ( block ) = = BLOCK_STATE_DELETED ) {
/* the parity is now updated without this block, so it's now empty */
fs_deallocate ( handle [ j ] . disk , blockcur ) ;
continue ;
2014-12-30 09:45:35 +01:00
}
2016-01-04 11:28:34 +01:00
/* now all the blocks have the hash and the parity computed */
block_state_set ( block , BLOCK_STATE_BLK ) ;
2011-09-14 22:52:55 +02:00
}
2013-06-23 22:54:54 +02:00
2013-09-29 21:01:31 +02:00
/* we update the info block only if we really have updated the parity */
2015-08-27 18:02:12 +02:00
/* because otherwise the time/justsynced info would be misleading as we didn't */
2013-09-29 21:01:31 +02:00
/* wrote the parity at this time */
2014-01-14 22:10:47 +01:00
/* we also update the info block only if no silent error was found */
/* because has no sense to refresh the time for data that we know bad */
if ( parity_needs_to_be_updated
& & ! silent_error_on_this_block
) {
2013-09-29 21:01:31 +02:00
/* if rehash is neeed */
if ( rehash ) {
/* store all the new hash already computed */
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
2013-09-29 21:01:31 +02:00
if ( rehandle [ j ] . block )
memcpy ( rehandle [ j ] . block - > hash , rehandle [ j ] . hash , HASH_SIZE ) ;
}
2013-07-01 23:08:25 +02:00
}
2013-09-29 21:01:31 +02:00
/* update the time info of the block */
/* we are also clearing any previous bad and rehash flag */
2015-12-30 23:45:11 +01:00
info_set ( & state - > infoarr , blockcur , info_make ( now , 0 , 0 , 1 ) ) ;
2013-09-29 21:01:31 +02:00
}
2014-01-14 22:10:47 +01:00
}
2014-12-30 09:45:35 +01:00
/* if a silent (even if corrected) or input/output error was found */
2014-01-14 22:10:47 +01:00
/* mark the block as bad to have check/fix to handle it */
/* because our correction is in memory only and not yet written */
2014-12-30 09:45:35 +01:00
if ( silent_error_on_this_block | | io_error_on_this_block ) {
2013-10-25 18:03:05 +02:00
/* set the error status keeping the other info */
2015-12-30 23:45:11 +01:00
info_set ( & state - > infoarr , blockcur , info_set_bad ( info ) ) ;
2011-03-18 22:38:11 +01:00
}
2011-03-04 20:59:21 +01:00
2016-01-04 11:28:34 +01:00
/* finally schedule parity write */
/* Note that the calls to io_parity_write() are mandatory */
/* even if the parity doesn't need to be updated */
/* This because we want to keep track of the time usage */
2016-01-26 23:25:01 +01:00
state_usage_misc ( state ) ;
2016-01-04 11:28:34 +01:00
/* write the parity */
for ( l = 0 ; l < state - > level ; + + l ) {
unsigned levcur ;
2016-01-20 21:30:00 +01:00
io_parity_write ( & io , & levcur , waiting_map , & waiting_mac ) ;
2016-01-04 11:28:34 +01:00
/* until now is parity */
2016-01-20 21:30:00 +01:00
state_usage_parity ( state , waiting_map , waiting_mac ) ;
2016-01-04 11:28:34 +01:00
}
/* write finished */
2016-01-05 13:20:25 +01:00
io_write_next ( & io , blockcur , ! parity_going_to_be_updated , writer_error ) ;
/* handle errors reported */
for ( j = 0 ; j < IO_WRITER_ERROR_MAX ; + + j ) {
if ( writer_error [ j ] ) {
switch ( j + IO_WRITER_ERROR_BASE ) {
case TASK_STATE_IOERROR_CONTINUE :
+ + io_error ;
if ( io_error > = state - > opt . io_error_limit ) {
log_fatal ( " DANGER! Unexpected input/output write error in a parity disk, it isn't possible to sync. \n " ) ;
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
goto bail ;
}
break ;
case TASK_STATE_ERROR_CONTINUE :
+ + error ;
break ;
case TASK_STATE_IOERROR :
+ + io_error ;
goto bail ;
case TASK_STATE_ERROR :
+ + error ;
goto bail ;
}
}
}
2016-01-04 11:28:34 +01:00
2011-03-19 22:00:08 +01:00
/* mark the state as needing write */
state - > need_write = 1 ;
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 , & io , blockcur , 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
}
2012-03-13 23:59:59 +01:00
/* autosave */
2014-11-10 23:06:14 +01:00
if ( ( state - > autosave ! = 0
2012-07-21 13:52:22 +02:00
& & autosavedone > = autosavelimit /* if we have reached the limit */
2014-11-10 23:06:14 +01:00
& & autosavemissing > = autosavelimit ) /* if we have at least a full step to do */
2014-11-12 20:47:07 +01:00
/* or if we have a forced autosave at the specified block */
2015-12-30 23:45:11 +01:00
| | ( state - > opt . force_autosave_at ! = 0 & & state - > opt . force_autosave_at = = blockcur )
2012-07-21 13:52:22 +02:00
) {
autosavedone = 0 ; /* restart the counter */
2012-03-13 23:59:59 +01:00
2016-01-26 23:25:01 +01:00
/* until now is misc */
state_usage_misc ( state ) ;
2014-08-02 10:15:23 +02:00
2012-03-13 23:59:59 +01:00
state_progress_stop ( state ) ;
2015-02-22 14:31:03 +01:00
msg_progress ( " Autosaving... \n " ) ;
2014-11-10 22:46:19 +01:00
/* before writing the new content file we ensure that */
/* the parity is really written flushing the disk cache */
for ( l = 0 ; l < state - > level ; + + l ) {
2016-01-04 11:32:02 +01:00
ret = parity_sync ( & parity_handle [ l ] ) ;
2014-11-10 22:46:19 +01:00
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
2015-12-30 23:45:11 +01:00
log_tag ( " parity_error:%u:%s: Sync error \n " , blockcur , lev_config_name ( l ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected sync error in %s disk. \n " , lev_name ( l ) ) ;
log_fatal ( " Ensure that disk '%s' is sane. \n " , lev_config_name ( l ) ) ;
2015-12-30 23:45:11 +01:00
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
2014-11-10 22:46:19 +01:00
+ + error ;
goto bail ;
/* LCOV_EXCL_STOP */
}
}
/* now we can safely write the content file */
2012-03-13 23:59:59 +01:00
state_write ( state ) ;
state_progress_restart ( state ) ;
2014-06-25 22:32:36 +02:00
/* drop until now */
state_usage_waste ( state ) ;
2012-03-13 23:59:59 +01:00
}
2011-03-04 20:59:21 +01:00
}
2014-09-27 00:23:05 +02:00
end :
2011-06-20 20:35:04 +02:00
state_progress_end ( state , countpos , countmax , countsize ) ;
2011-03-04 20:59:21 +01:00
2014-06-25 22:32:36 +02:00
state_usage_print ( state ) ;
2015-01-31 16:44:46 +01:00
/* before returning we ensure that */
/* the parity is really written flushing the disk cache */
for ( l = 0 ; l < state - > level ; + + l ) {
2016-01-04 11:32:02 +01:00
ret = parity_sync ( & parity_handle [ l ] ) ;
2015-01-31 16:44:46 +01:00
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
2015-12-30 23:45:11 +01:00
log_tag ( " parity_error:%u:%s: Sync error \n " , blockcur , lev_config_name ( l ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected sync error in %s disk. \n " , lev_name ( l ) ) ;
log_fatal ( " Ensure that disk '%s' is sane. \n " , lev_config_name ( l ) ) ;
2015-12-30 23:45:11 +01:00
log_fatal ( " Stopping at block %u \n " , blockcur ) ;
2015-01-31 16:44:46 +01:00
+ + error ;
goto bail ;
/* LCOV_EXCL_STOP */
}
}
2014-12-27 23:14:06 +01:00
if ( error | | silent_error | | io_error ) {
2015-02-22 14:31:03 +01:00
msg_status ( " \n " ) ;
msg_status ( " %8u file errors \n " , error ) ;
msg_status ( " %8u io errors \n " , io_error ) ;
msg_status ( " %8u data errors \n " , silent_error ) ;
2013-09-21 19:11:11 +02:00
} else {
/* print the result only if processed something */
if ( countpos ! = 0 )
2015-02-22 14:31:03 +01:00
msg_status ( " Everything OK \n " ) ;
}
if ( error )
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Unexpected file errors! \n " ) ;
2015-02-22 14:31:03 +01:00
if ( io_error )
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected input/output errors! The failing blocks are now marked as bad! \n " ) ;
2015-02-22 14:31:03 +01:00
if ( silent_error )
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected data errors! The failing blocks are now marked as bad! \n " ) ;
2015-02-22 14:31:03 +01:00
if ( io_error | | silent_error ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " Use 'snapraid status' to list the bad blocks. \n " ) ;
log_fatal ( " Use 'snapraid -e fix' to recover. \n " ) ;
2013-09-21 19:11:11 +02:00
}
2015-04-20 22:07:42 +02:00
log_tag ( " summary:error_file:%u \n " , error ) ;
log_tag ( " summary:error_io:%u \n " , io_error ) ;
log_tag ( " summary:error_data:%u \n " , silent_error ) ;
2014-12-27 23:14:06 +01:00
if ( error + silent_error + io_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 " ) ;
log_flush ( ) ;
2013-09-30 22:24:30 +02:00
2011-03-18 22:38:11 +01:00
bail :
2015-12-30 23:45:11 +01:00
/* stop all the worker threads */
io_stop ( & io ) ;
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
2015-02-03 18:12:52 +01:00
struct snapraid_file * file = handle [ j ] . file ;
struct snapraid_disk * disk = handle [ j ] . disk ;
2011-03-18 22:38:11 +01:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
2014-12-27 23:14:06 +01:00
/* LCOV_EXCL_START */
2015-12-30 23:45:11 +01:00
log_tag ( " error:%u:%s:%s: Close error. %s \n " , blockcur , disk - > name , esc ( file - > sub ) , strerror ( errno ) ) ;
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! Unexpected close error in a data disk. \n " ) ;
2013-09-21 19:11:11 +02:00
+ + error ;
2011-03-18 22:38:11 +01:00
/* continue, as we are already exiting */
2014-12-27 23:14:06 +01:00
/* LCOV_EXCL_STOP */
2011-03-18 22:38:11 +01:00
}
2011-03-04 20:59:21 +01:00
}
free ( handle ) ;
2015-12-30 23:45:11 +01:00
free ( zero_alloc ) ;
2016-01-05 14:48:39 +01:00
free ( copy_alloc ) ;
free ( copy ) ;
2013-07-01 23:08:25 +02:00
free ( rehandle_alloc ) ;
2014-01-14 22:10:47 +01:00
free ( failed ) ;
free ( failed_map ) ;
2016-01-20 21:30:00 +01:00
free ( waiting_map ) ;
2015-12-30 23:45:11 +01:00
io_done ( & io ) ;
2011-03-19 22:00:08 +01:00
2013-09-21 19:11:11 +02:00
if ( state - > opt . expect_recoverable ) {
2014-12-27 23:14:06 +01:00
if ( error + silent_error + io_error = = 0 )
2013-09-21 19:11:11 +02:00
return - 1 ;
} else {
2014-12-27 23:14:06 +01:00
if ( error + silent_error + io_error ! = 0 )
2013-09-21 19:11:11 +02:00
return - 1 ;
}
2011-03-19 22:00:08 +01:00
return 0 ;
2011-03-04 20:59:21 +01:00
}
2011-03-23 18:54:08 +01:00
int state_sync ( struct snapraid_state * state , 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 ;
2015-02-01 18:48:50 +01:00
block_off_t used_paritymax ;
block_off_t file_paritymax ;
2011-03-12 10:09:59 +01:00
data_off_t size ;
2012-10-09 23:02:46 +02:00
data_off_t out_size ;
2011-03-18 22:38:11 +01:00
int ret ;
2016-01-04 11:32:02 +01:00
struct snapraid_parity_handle parity_handle [ LEV_MAX ] ;
2011-03-19 22:00:08 +01:00
unsigned unrecoverable_error ;
2013-09-15 21:12:41 +02:00
unsigned l ;
2014-02-08 16:49:24 +01:00
int skip_sync = 0 ;
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 ;
2015-02-01 18:48:50 +01:00
/* minum size of the parity files we expect */
used_paritymax = parity_used_size ( state ) ;
/* effective size of the parity files */
file_paritymax = 0 ;
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 */
2015-04-20 22:07:42 +02:00
log_fatal ( " Error in the starting block %u. It's bigger than the parity size %u. \n " , blockstart , blockmax ) ;
2011-03-04 20:59:21 +01:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-03-04 20:59:21 +01:00
}
2011-04-23 21:38:26 +02: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
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2015-02-01 18:48:50 +01:00
block_off_t parityblocks ;
2013-09-15 21:12:41 +02:00
/* create the file and open for writing */
2016-01-04 11:32:02 +01:00
ret = parity_create ( & parity_handle [ l ] , l , state - > parity [ l ] . path , & out_size , state - > file_mode ) ;
2012-10-09 23:02:46 +02:00
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! Without an accessible %s file, it isn't possible to sync. \n " , lev_name ( l ) ) ;
2012-10-09 23:02:46 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2012-10-09 23:02:46 +02:00
}
2015-02-01 18:48:50 +01:00
/* number of block in the parity file */
parityblocks = out_size / state - > block_size ;
/* if the file is too small */
if ( parityblocks < used_paritymax ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " WARNING! The %s file %s has data only %u blocks instead of %u. \n " , lev_name ( l ) , state - > parity [ l ] . path , parityblocks , used_paritymax ) ;
2015-02-01 18:48:50 +01:00
}
/* keep the smallest parity number of blocks */
if ( l = = 0 | | file_paritymax > parityblocks )
file_paritymax = parityblocks ;
}
/* if we do a full sync, having a wrong parity size is expected */
if ( ! state - > opt . force_full ) {
/* if the parities are too small */
if ( file_paritymax < used_paritymax ) {
/* LCOV_EXCL_START */
2015-04-20 22:07:42 +02:00
log_fatal ( " DANGER! One or more the parity files are smaller than expected! \n " ) ;
2015-02-01 18:48:50 +01:00
if ( file_paritymax ! = 0 ) {
2015-04-20 22:07:42 +02:00
log_fatal ( " If this happens because you are using an old content file, \n " ) ;
log_fatal ( " you can 'sync' anyway using 'snapraid --force-full sync' \n " ) ;
log_fatal ( " to force a full rebuild of the parity. \n " ) ;
2015-02-01 18:48:50 +01:00
} else {
2015-04-20 22:07:42 +02:00
log_fatal ( " It's possible that the parity disks are not mounted. \n " ) ;
log_fatal ( " If instead you are adding a new parity level, you can 'sync' using \n " ) ;
log_fatal ( " 'snapraid --force-full sync' to force a full rebuild of the parity. \n " ) ;
2014-06-15 19:01:07 +02:00
}
2015-02-01 18:48:50 +01:00
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
2012-10-09 23:02:46 +02:00
}
2015-02-01 18:48:50 +01:00
}
2012-10-09 23:02:46 +02:00
2014-02-08 16:49:24 +01:00
unrecoverable_error = 0 ;
2014-02-10 20:53:47 +01:00
if ( state - > opt . prehash ) {
2015-02-22 14:31:03 +01:00
msg_progress ( " Hashing... \n " ) ;
2014-02-02 23:03:37 +01:00
2014-12-27 23:14:06 +01:00
ret = state_hash_process ( state , blockstart , blockmax , & skip_sync ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
+ + unrecoverable_error ;
2016-01-10 23:00:11 +01:00
/* continue, in case also doing the sync if ::skip_sync is not set */
2014-12-27 23:14:06 +01:00
/* LCOV_EXCL_STOP */
}
2016-01-10 23:00:11 +01:00
}
2014-02-02 23:03:37 +01:00
2016-01-10 23:00:11 +01:00
if ( ! skip_sync ) {
/**
* Save the new state before the sync but after the hashing phase
*
* This allows to recover after an aborted sync , and at the same time
* it allows to recover broken copied / moved files identified in the
* hashing phase .
*
* For example , think at this case :
* - Add some files at the array
* - Run a sync command , it will recompute the parity adding the new files
* - Abort the sync command before it stores the new content file
* - Delete the not yet synced files from the array
* - Run a new sync command
*
* The sync command has no way to know that the parity file was modified
* because the files triggering these changes are now deleted and they aren ' t
* listed in the content file .
* Instead , saving the new content file in advance , keeps track of all the parity
* that may be modified .
*/
2014-02-02 23:03:37 +01:00
if ( state - > need_write )
state_write ( state ) ;
2016-01-10 23:00:11 +01:00
/* now change the size of all parities */
for ( l = 0 ; l < state - > level ; + + l ) {
/* change the size of the parity file, truncating or extending it */
/* from this point all the DELETED blocks after the end of the parity are invalid */
/* and they are automatically removed when we save the new content file */
ret = parity_chsize ( & parity_handle [ l ] , size , & out_size , state - > opt . skip_fallocate ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
parity_overflow ( state , out_size ) ;
log_fatal ( " WARNING! Without an accessible %s file, it isn't possible to sync. \n " , lev_name ( l ) ) ;
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
}
/* after resizing parity files, refresh again the free info */
state_refresh ( state ) ;
2015-02-22 14:31:03 +01:00
msg_progress ( " Syncing... \n " ) ;
2011-03-19 22:00:08 +01:00
2014-02-08 16:49:24 +01:00
/* skip degenerated cases of empty parity, or skipping all */
if ( blockstart < blockmax ) {
2016-01-04 11:32:02 +01:00
ret = state_sync_process ( state , parity_handle , blockstart , blockmax ) ;
2014-02-08 16:49:24 +01:00
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
+ + unrecoverable_error ;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
} else {
2015-02-22 14:31:03 +01:00
msg_status ( " Nothing to do \n " ) ;
2011-03-19 22:00:08 +01:00
}
2011-03-17 16:17:59 +01:00
}
2011-03-04 20:59:21 +01:00
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
2016-01-04 11:32:02 +01:00
ret = parity_close ( & parity_handle [ l ] ) ;
2013-09-11 23:30:32 +02:00
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01: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
+ + unrecoverable_error ;
/* continue, as we are already exiting */
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-09-11 23:30:32 +02:00
}
}
2011-03-19 22:00:08 +01:00
/* abort if required */
if ( unrecoverable_error ! = 0 )
return - 1 ;
return 0 ;
2011-03-04 20:59:21 +01:00
}
2011-03-06 22:52:50 +01:00