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"
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 ;
2014-12-27 23:14:06 +01:00
unsigned io_error ;
2014-02-02 23:03:37 +01:00
/* maps the disks to handles */
handle = handle_map ( state , & diskmax ) ;
/* 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 ;
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 ;
block = disk_block_get ( disk , i ) ;
/* if no file in this block, skip it */
if ( ! block_has_file ( block ) )
continue ;
/* if the block already has a hash, skip it */
if ( block_has_updated_hash ( block ) )
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 ] ;
block = disk_block_get ( disk , i ) ;
/* if no file in this block, skip it */
if ( ! block_has_file ( block ) )
continue ;
/* if the block already has a hash, skip it */
if ( block_has_updated_hash ( block ) )
continue ;
/* here only CHG blocks are allowed */
assert ( block_state_get ( block ) = = BLOCK_STATE_CHG ) ;
/* get block specific info */
info = info_get ( & state - > infoarr , i ) ;
/* if we have to use the old hash */
rehash = info_get_rehash ( info ) ;
2014-06-25 22:32:36 +02:00
/* until now is CPU */
state_usage_cpu ( state ) ;
2014-02-02 23:03:37 +01:00
/* if the file is different than the current one, close it */
if ( handle [ j ] . file ! = 0 & & handle [ j ] . file ! = block_file_get ( block ) ) {
2014-12-27 23:14:06 +01:00
/* keep a pointer at the file we are going to close for error reporting */
2014-02-02 23:03:37 +01:00
struct snapraid_file * file = handle [ j ] . file ;
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-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Close EIO error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle [ j ] . path ) ;
2014-12-27 23:14:06 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + io_error ;
goto bail ;
}
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Close error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected close error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that file '%s' can be accessed. \n " , handle [ j ] . path ) ;
2014-02-02 23:03:37 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2014-02-02 23:03:37 +01:00
}
}
2015-02-02 23:39:46 +01:00
ret = handle_open ( & handle [ j ] , block_file_get ( block ) , state - > file_mode , flog ) ;
2014-02-02 23:03:37 +01:00
if ( ret = = - 1 ) {
2014-12-27 23:14:06 +01:00
/* file we have tried to open for error reporting */
2014-02-02 23:03:37 +01:00
struct snapraid_file * file = block_file_get ( block ) ;
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
/* LCOV_EXCL_START */
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Open EIO error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " DANGER! Unexpected input/output open error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle [ j ] . path ) ;
2014-12-27 23:14:06 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + io_error ;
goto bail ;
/* LCOV_EXCL_STOP */
}
2014-02-02 23:03:37 +01:00
if ( errno = = ENOENT ) {
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Open ENOENT error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
flog ( " Missing file '%s'. \n " , handle [ j ] . path ) ;
flog ( " WARNING! You cannot modify data disk during a sync. \n " ) ;
flog ( " 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-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Open EACCES error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
flog ( " No access at file '%s'. \n " , handle [ j ] . path ) ;
flog ( " WARNING! Please fix the access permission in the data disk. \n " ) ;
flog ( " 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-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Open error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected open error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that file '%s' can be accessed. \n " , handle [ j ] . path ) ;
2014-04-07 23:12:40 +02:00
printf ( " 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 */
if ( handle [ j ] . st . st_size ! = block_file_get ( block ) - > size
| | handle [ j ] . st . st_mtime ! = block_file_get ( block ) - > mtime_sec
| | STAT_NSEC ( & handle [ j ] . st ) ! = block_file_get ( block ) - > mtime_nsec
| | handle [ j ] . st . st_ino ! = block_file_get ( block ) - > inode
) {
2014-04-04 23:06:00 +02:00
/* file we are processing for error reporting */
struct snapraid_file * file = block_file_get ( block ) ;
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Unexpected attribute change \n " , i , disk - > name , file - > sub ) ;
2014-04-04 23:06:00 +02:00
if ( handle [ j ] . st . st_size ! = file - > size ) {
2015-02-02 23:39:46 +01:00
flog ( " 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-02-02 23:39:46 +01:00
flog ( " 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-02-02 23:39:46 +01:00
flog ( " 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-02-02 23:39:46 +01:00
flog ( " WARNING! You cannot modify files during a sync. \n " ) ;
flog ( " 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-02-02 23:39:46 +01:00
read_size = handle_read ( & handle [ j ] , block , buffer , state - > block_size , ferr ) ;
2014-02-02 23:03:37 +01:00
if ( read_size = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2014-04-04 23:06:00 +02:00
/* file we are processing for error reporting */
struct snapraid_file * file = block_file_get ( block ) ;
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Read EIO error at position %u. %s \n " , i , disk - > name , file - > sub , block_file_pos ( block ) , strerror ( errno ) ) ;
ferr ( " DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that disk '%s' is sane and that file '%s' can be read. \n " , disk - > dir , handle [ j ] . path ) ;
2014-12-27 23:14:06 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + io_error ;
goto bail ;
}
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Read error at position %u. %s \n " , i , disk - > name , file - > sub , block_file_pos ( block ) , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected read error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that file '%s' can be read. \n " , handle [ j ] . path ) ;
2014-04-04 23:06:00 +02:00
printf ( " 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 */
state_usage_disk ( state , disk ) ;
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 ) ;
}
/* 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 ;
/* count the number of processed block */
+ + countpos ;
/* progress */
if ( state_progress ( state , 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 */
2014-02-02 23:03:37 +01:00
struct snapraid_file * file = handle [ j ] . file ;
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-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Close EIO error. %s \n " , blockmax , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle [ j ] . path ) ;
2014-12-27 23:14:06 +01:00
printf ( " Stopping at block %u \n " , blockmax ) ;
+ + io_error ;
goto bail ;
}
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Close error. %s \n " , blockmax , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected close error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that file '%s' can be accessed. \n " , handle [ j ] . path ) ;
2014-02-02 23:03:37 +01:00
printf ( " Stopping at block %u \n " , blockmax ) ;
+ + 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 ) ;
2014-12-27 23:14:06 +01:00
if ( error | | io_error ) {
2014-02-02 23:03:37 +01:00
printf ( " \n " ) ;
2014-12-27 23:14:06 +01:00
printf ( " %8u file errors \n " , error ) ;
printf ( " %8u io errors \n " , io_error ) ;
2014-02-02 23:03:37 +01:00
printf ( " WARNING! There are errors! \n " ) ;
} else {
/* print the result only if processed something */
if ( countpos ! = 0 )
printf ( " Everything OK \n " ) ;
}
2015-02-02 23:39:46 +01:00
ftag ( " hash_summary:error_readwrite:%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 ) {
2014-02-02 23:03:37 +01:00
ret = handle_close ( & handle [ j ] ) ;
if ( ret = = - 1 ) {
2015-02-02 23:39:46 +01:00
ferr ( " 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
2014-12-27 23:14:06 +01:00
if ( error + io_error ! = 0 )
return - 1 ;
return 0 ;
2014-02-02 23:03:37 +01:00
}
2011-03-04 20:59:21 +01:00
/****************************************************************************/
/* sync */
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 */
} ;
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 .
*/
static int block_is_enabled ( block_off_t i , struct snapraid_handle * handle , unsigned diskmax )
{
unsigned j ;
int one_invalid ;
int one_valid ;
/* for each disk */
one_invalid = 0 ;
one_valid = 0 ;
for ( j = 0 ; j < diskmax ; + + j ) {
struct snapraid_block * block ;
/* if no disk, nothing to check */
if ( ! handle [ j ] . disk )
continue ;
block = disk_block_get ( handle [ j ] . disk , i ) ;
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 ;
}
2014-11-09 10:25:39 +01:00
static int state_sync_process ( struct snapraid_state * state , 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 ;
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 ;
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-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 ;
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 */
handle = handle_map ( state , & diskmax ) ;
2013-07-01 23:08:25 +02:00
/* rehash buffers */
rehandle = malloc_nofail_align ( diskmax * sizeof ( struct snapraid_rehash ) , & rehandle_alloc ) ;
2014-01-14 22:10:47 +01:00
/* we need 2 * data + 1 * parity + 1 * zero */
buffermax = 2 * diskmax + 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 ) ;
2011-03-04 20:59:21 +01:00
2014-01-14 22:10:47 +01: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 ] ) ;
2014-01-14 22:10:47 +01:00
failed = malloc_nofail ( diskmax * sizeof ( struct failed_struct ) ) ;
failed_map = malloc_nofail ( diskmax * sizeof ( unsigned ) ) ;
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 ;
2014-09-27 00:32:39 +02:00
for ( i = blockstart ; i < blockmax ; + + i ) {
2014-12-29 19:11:03 +01:00
if ( ! block_is_enabled ( i , handle , diskmax ) )
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 ;
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 ( i = blockstart ; i < blockmax ; + + i ) {
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 ;
2013-07-01 23:08:25 +02:00
snapraid_info info ;
int rehash ;
2011-03-04 20:59:21 +01:00
2014-12-29 19:11:03 +01:00
if ( ! block_is_enabled ( i , handle , diskmax ) )
2011-03-04 20:59:21 +01:00
continue ;
2012-07-21 13:52:22 +02:00
/* one more block processed for autosave */
+ + autosavedone ;
- - autosavemissing ;
2011-09-14 22:52:55 +02:00
/* by default process the block, and skip it if something go 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 */
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 ) ;
2013-09-29 21:01:31 +02:00
/* it could happens that all the blocks are EMPTY/BLK and CHG but with the hash */
/* still matching because the specific CHG block was not modified. */
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. */
2013-09-29 21:01:31 +02:00
/* In such case, we can avoid to update parity, because it would be the same as before */
parity_needs_to_be_updated = 0 ;
/* 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 ) {
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 ;
2014-04-04 23:06:00 +02:00
struct snapraid_disk * disk = handle [ j ] . disk ;
2011-03-04 20:59:21 +01:00
2013-07-01 23:08:25 +02:00
/* by default no rehash in case of "continue" */
rehandle [ j ] . block = 0 ;
2011-10-01 19:18:32 +02:00
/* if the disk position is not used */
2014-04-04 23:06:00 +02:00
if ( ! disk ) {
2011-10-01 19:18:32 +02:00
/* use an empty block */
memset ( buffer [ j ] , 0 , state - > block_size ) ;
continue ;
}
2013-09-29 21:01:31 +02:00
/* get the block */
2014-04-04 23:06:00 +02:00
block = disk_block_get ( disk , i ) ;
2013-09-29 21:01:31 +02:00
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 */
2014-01-14 22:10:47 +01:00
failed [ failed_count ] . index = j ;
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
}
2014-01-14 22:10:47 +01:00
/* if the block has no file, meanining that it's EMPTY or DELETED, */
/* it doesn't partecipate in the new parity computation */
2013-02-24 19:02:53 +01:00
if ( ! block_has_file ( block ) ) {
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
2014-06-25 22:32:36 +02:00
/* until now is CPU */
state_usage_cpu ( state ) ;
2011-08-31 23:33:43 +02:00
/* if the file is different than the current one, close it */
2013-12-03 18:40:19 +01:00
if ( handle [ j ] . file ! = 0 & & handle [ j ] . file ! = block_file_get ( block ) ) {
2014-12-27 23:14:06 +01:00
/* keep a pointer at the file we are going to close for error reporting */
2013-12-03 18:40:19 +01:00
struct snapraid_file * file = handle [ j ] . file ;
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 */
2011-08-31 23:33:43 +02: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-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Close EIO error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " DANGER! Unexpected input/output close error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle [ j ] . path ) ;
2014-12-27 23:14:06 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + io_error ;
goto bail ;
}
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Close error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected close error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that file '%s' can be accessed. \n " , handle [ j ] . path ) ;
2011-08-31 23:33:43 +02:00
printf ( " Stopping at block %u \n " , i ) ;
2013-09-21 19:11:11 +02:00
+ + error ;
2011-08-31 23:33:43 +02:00
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
}
2015-02-02 23:39:46 +01:00
ret = handle_open ( & handle [ j ] , block_file_get ( block ) , state - > file_mode , flog ) ;
2011-03-18 22:38:11 +01:00
if ( ret = = - 1 ) {
2014-12-27 23:14:06 +01:00
/* file we have tried to open for error reporting */
2013-12-03 18:40:19 +01:00
struct snapraid_file * file = block_file_get ( block ) ;
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
/* LCOV_EXCL_START */
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Open EIO error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " DANGER! Unexpected input/output open error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that disk '%s' is sane and that file '%s' can be accessed. \n " , disk - > dir , handle [ j ] . path ) ;
2014-12-27 23:14:06 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + io_error ;
goto bail ;
/* LCOV_EXCL_STOP */
}
2011-03-19 22:00:08 +01:00
if ( errno = = ENOENT ) {
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Open ENOENT error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
flog ( " Missing file '%s'. \n " , handle [ j ] . path ) ;
flog ( " WARNING! You cannot modify data disk during a sync. \n " ) ;
flog ( " Rerun the sync command when finished. \n " ) ;
2013-09-21 19:11:11 +02:00
+ + error ;
2011-09-14 22:52:55 +02:00
/* 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 */
2013-10-03 22:16:29 +02:00
error_on_this_block = 1 ;
2011-09-14 22:52:55 +02:00
continue ;
2014-02-05 00:00:29 +01:00
}
2014-12-27 23:14:06 +01:00
2014-02-05 00:00:29 +01:00
if ( errno = = EACCES ) {
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Open EACCES error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
flog ( " No access at file '%s'. \n " , handle [ j ] . path ) ;
flog ( " WARNING! Please fix the access permission in the data disk. \n " ) ;
flog ( " Rerun the sync command when finished. \n " ) ;
2013-10-21 21:56:53 +02:00
+ + error ;
/* this isn't a serious error, so we skip this block, and continue with others */
error_on_this_block = 1 ;
continue ;
2011-03-19 22:00:08 +01:00
}
2014-09-27 00:32:39 +02:00
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Open error. %s \n " , i , disk - > name , file - > sub , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected open error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that file '%s' can be accessed. \n " , handle [ j ] . path ) ;
2014-02-05 00:00:29 +01:00
printf ( " Stopping to allow recovery. Try with 'snapraid check -f %s' \n " , file - > sub ) ;
2013-09-21 19:11:11 +02:00
+ + error ;
2011-03-19 22:00:08 +01:00
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-03-19 22:00:08 +01:00
}
/* check if the file is changed */
2011-04-28 23:09:19 +02:00
if ( handle [ j ] . st . st_size ! = block_file_get ( block ) - > size
2012-07-10 23:24:31 +02:00
| | handle [ j ] . st . st_mtime ! = block_file_get ( block ) - > mtime_sec
2012-09-19 21:52:15 +02:00
| | STAT_NSEC ( & handle [ j ] . st ) ! = block_file_get ( block ) - > mtime_nsec
2011-04-28 23:09:19 +02:00
| | handle [ j ] . st . st_ino ! = block_file_get ( block ) - > inode
2011-03-20 22:36:49 +01:00
) {
2014-04-04 23:06:00 +02:00
/* file we are processing for error reporting */
struct snapraid_file * file = block_file_get ( block ) ;
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Unexpected attribute change \n " , i , disk - > name , file - > sub ) ;
2014-04-07 22:05:25 +02:00
if ( handle [ j ] . st . st_size ! = file - > size ) {
2015-02-02 23:39:46 +01:00
flog ( " Unexpected size change at file '%s' from % " PRIu64 " to % " PRIu64 " . \n " , handle [ j ] . path , file - > size , handle [ j ] . st . st_size ) ;
2014-04-07 22:05:25 +02:00
} else if ( handle [ j ] . st . st_mtime ! = file - > mtime_sec
| | STAT_NSEC ( & handle [ j ] . st ) ! = file - > mtime_nsec ) {
2015-02-02 23:39:46 +01:00
flog ( " 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 ) ) ;
2013-11-14 21:12:48 +01:00
} else {
2015-02-02 23:39:46 +01:00
flog ( " Unexpected inode change from % " PRIu64 " to % " PRIu64 " at file '%s'. \n " , file - > inode , ( uint64_t ) handle [ j ] . st . st_ino , handle [ j ] . path ) ;
2013-11-14 21:12:48 +01:00
}
2015-02-02 23:39:46 +01:00
flog ( " WARNING! You cannot modify files during a sync. \n " ) ;
flog ( " Rerun the sync command when finished. \n " ) ;
2013-09-21 19:11:11 +02:00
+ + error ;
2012-07-10 23:24:31 +02:00
/* if the file is changed, it means that it was modified during sync */
2011-09-14 22:52:55 +02:00
/* this isn't a serious error, so we skip this block, and continue with others */
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
}
2011-03-04 20:59:21 +01:00
2015-02-02 23:39:46 +01:00
read_size = handle_read ( & handle [ j ] , block , buffer [ j ] , state - > block_size , flog ) ;
2011-03-18 22:38:11 +01:00
if ( read_size = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2014-04-04 23:06:00 +02:00
/* file we are processing for error reporting */
struct snapraid_file * file = block_file_get ( block ) ;
2014-12-27 23:14:06 +01:00
if ( errno = = EIO ) {
2015-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Read EIO error at position %u. %s \n " , i , disk - > name , file - > sub , block_file_pos ( block ) , strerror ( errno ) ) ;
2014-12-30 09:45:35 +01:00
if ( io_error > = state - > opt . io_error_limit ) {
2015-02-02 23:39:46 +01:00
ferr ( " DANGER! Unexpected input/output read error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that disk '%s' is sane and that file '%s' can be read. \n " , disk - > dir , handle [ j ] . path ) ;
2014-12-30 09:45:35 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + io_error ;
goto bail ;
}
2015-02-02 23:39:46 +01:00
flog ( " Input/Output error in file '%s' at position '%u' \n " , handle [ j ] . path , block_file_pos ( block ) ) ;
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-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Read error at position %u. %s \n " , i , disk - > name , file - > sub , block_file_pos ( block ) , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected read error in a data disk, it isn't possible to sync. \n " ) ;
ferr ( " Ensure that file '%s' can be read. \n " , handle [ j ] . path ) ;
2014-04-04 23:06:00 +02:00
printf ( " Stopping to allow recovery. Try with 'snapraid check -f %s' \n " , file - > sub ) ;
2013-09-21 19:11:11 +02:00
+ + error ;
2011-03-18 22:38:11 +01:00
goto bail ;
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
2014-06-25 22:32:36 +02:00
/* until now is disk */
state_usage_disk ( state , disk ) ;
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 ) {
memhash ( state - > prevhash , state - > prevhashseed , hash , buffer [ j ] , read_size ) ;
/* compute the new hash, and store it */
rehandle [ j ] . block = block ;
memhash ( state - > hash , state - > hashseed , rehandle [ j ] . hash , buffer [ j ] , read_size ) ;
} else {
memhash ( state - > hash , state - > hashseed , hash , buffer [ j ] , read_size ) ;
}
2011-03-04 20:59:21 +01:00
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-04-04 23:06:00 +02:00
/* file we are processing for error reporting */
struct snapraid_file * file = block_file_get ( block ) ;
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-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Unexpected data change \n " , i , disk - > name , file - > sub ) ;
flog ( " Data change at file '%s' at position '%u' \n " , handle [ j ] . path , block_file_pos ( block ) ) ;
flog ( " 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-02-02 23:39:46 +01:00
flog ( " This file was detected as a copy of another file with the same name, size, \n " ) ;
flog ( " and timestamp, but the file data isn't matching the assumed copy. \n " ) ;
flog ( " If this is a false positive, and the files are expected to be different, \n " ) ;
flog ( " you can 'sync' anyway using 'snapraid --force-nocopy sync' \n " ) ;
2014-05-15 23:45:51 +02:00
} else {
2015-02-02 23:39:46 +01:00
flog ( " 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-02-02 23:39:46 +01:00
ftag ( " error:%u:%s:%s: Data error at position %u \n " , i , disk - > name , file - > sub , block_file_pos ( block ) ) ;
flog ( " Data error in file '%s' at position '%u' \n " , handle [ j ] . path , block_file_pos ( block ) ) ;
2014-02-01 17:57:26 +01:00
/* save the failed block for the fix */
failed [ failed_count ] . index = j ;
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
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 ] ;
unsigned char * block_copy = buffer [ diskmax + state - > level + failed [ j ] . index ] ;
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 ) {
2014-06-25 22:32:36 +02:00
/* until now is CPU */
state_usage_cpu ( state ) ;
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 ) {
2015-02-02 23:39:46 +01:00
ret = parity_read ( parity [ l ] , i , buffer [ diskmax + l ] , state - > block_size , flog ) ;
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-02-02 23:39:46 +01:00
ftag ( " parity_error:%u:%s: Read EIO error. %s \n " , i , lev_config_name ( l ) , strerror ( errno ) ) ;
2014-12-30 09:45:35 +01:00
if ( io_error > = state - > opt . io_error_limit ) {
2015-02-02 23:39:46 +01:00
ferr ( " DANGER! Unexpected input/output read error in the %s disk, it isn't possible to sync. \n " , lev_name ( l ) ) ;
ferr ( " Ensure that disk '%s' is sane and can be read. \n " , lev_config_name ( l ) ) ;
2014-12-30 09:45:35 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + io_error ;
goto bail ;
}
2015-02-02 23:39:46 +01:00
flog ( " Input/Output error in parity '%s' at position '%u' \n " , lev_config_name ( l ) , i ) ;
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-02-02 23:39:46 +01:00
ftag ( " parity_error:%u:%s: Read error. %s \n " , i , lev_config_name ( l ) , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected read error in the %s disk, it isn't possible to sync. \n " , lev_name ( l ) ) ;
ferr ( " Ensure that disk '%s' can be read. \n " , lev_config_name ( l ) ) ;
2014-01-14 22:10:47 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + 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 */
state_usage_parity ( state , l ) ;
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 ) ;
/* 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 ] ;
unsigned char * block_copy = buffer [ diskmax + state - > level + failed [ j ] . index ] ;
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 ) ;
}
/* 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 )
) {
2013-09-29 21:01:31 +02:00
/* updates the parity only if really needed */
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
2014-06-25 22:32:36 +02:00
/* until now is CPU */
state_usage_cpu ( state ) ;
2013-09-29 21:01:31 +02:00
/* write the parity */
2014-09-27 00:32:39 +02:00
for ( l = 0 ; l < state - > level ; + + l ) {
ret = parity_write ( parity [ l ] , i , buffer [ diskmax + l ] , state - > block_size ) ;
2013-09-29 21:01:31 +02: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-02-02 23:39:46 +01:00
ftag ( " parity_error:%u:%s: Write EIO error. %s \n " , i , lev_config_name ( l ) , strerror ( errno ) ) ;
2014-12-30 09:45:35 +01:00
if ( io_error > = state - > opt . io_error_limit ) {
2015-02-02 23:39:46 +01:00
ferr ( " DANGER! Unexpected input/output write error in the %s disk, it isn't possible to sync. \n " , lev_name ( l ) ) ;
ferr ( " Ensure that disk '%s' is sane and can be written. \n " , lev_config_name ( l ) ) ;
2014-12-30 09:45:35 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + io_error ;
goto bail ;
}
2015-02-02 23:39:46 +01:00
flog ( " Input/Output error in parity '%s' at position '%u' \n " , lev_config_name ( l ) , i ) ;
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-02-02 23:39:46 +01:00
ftag ( " parity_error:%u:%s: Write error. %s \n " , i , lev_config_name ( l ) , strerror ( errno ) ) ;
ferr ( " WARNING! Unexpected write error in the %s disk, it isn't possible to sync. \n " , lev_name ( l ) ) ;
ferr ( " Ensure that disk '%s' has some free space available. \n " , lev_config_name ( l ) ) ;
2013-09-29 21:01:31 +02:00
printf ( " Stopping at block %u \n " , i ) ;
+ + error ;
goto bail ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-09-29 21:01:31 +02:00
}
2014-06-25 22:32:36 +02:00
/* until now is parity */
state_usage_parity ( state , l ) ;
2013-09-11 23:30:32 +02:00
}
}
2014-12-30 09:45:35 +01:00
/* if no error in parity write */
if ( ! io_error_on_this_block ) {
/* for each disk, mark the blocks as processed */
for ( j = 0 ; j < diskmax ; + + j ) {
struct snapraid_block * block = BLOCK_EMPTY ;
if ( handle [ j ] . disk )
block = disk_block_get ( handle [ j ] . disk , i ) ;
2012-02-09 23:17:36 +01:00
2014-12-30 09:45:35 +01:00
if ( block = = BLOCK_EMPTY ) {
/* nothing to do */
continue ;
}
2012-02-09 23:17:36 +01:00
2014-12-30 09:45:35 +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 */
tommy_arrayblk_set ( & handle [ j ] . disk - > blockarr , i , BLOCK_EMPTY ) ;
continue ;
}
2011-03-18 22:38:11 +01:00
2014-12-30 09:45:35 +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 */
/* because otherwise the time info would be misleading as we didn't */
/* 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
2014-12-30 09:45:35 +01:00
& & ! io_error_on_this_block
2014-01-14 22:10:47 +01:00
) {
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 */
info_set ( & state - > infoarr , i , info_make ( now , 0 , 0 ) ) ;
}
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 */
2013-10-03 22:16:29 +02:00
info_set ( & state - > infoarr , i , info_set_bad ( info ) ) ;
2011-03-18 22:38:11 +01:00
}
2011-03-04 20:59:21 +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 */
2011-06-20 20:35:04 +02:00
if ( state_progress ( state , 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
}
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 */
2014-11-10 23:06:14 +01:00
| | ( state - > opt . force_autosave_at ! = 0 & & state - > opt . force_autosave_at = = i )
2012-07-21 13:52:22 +02:00
) {
autosavedone = 0 ; /* restart the counter */
2012-03-13 23:59:59 +01:00
2014-08-02 10:15:23 +02:00
/* until now is CPU */
state_usage_cpu ( state ) ;
2012-03-13 23:59:59 +01:00
state_progress_stop ( state ) ;
printf ( " 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 ) {
ret = parity_sync ( parity [ l ] ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
2015-02-02 23:39:46 +01:00
ftag ( " parity_error:%u:%s: Sync error \n " , i , lev_config_name ( l ) ) ;
ferr ( " DANGER! Unexpected sync error in %s disk. \n " , lev_name ( l ) ) ;
ferr ( " Ensure that disk '%s' is sane. \n " , lev_config_name ( l ) ) ;
2014-11-10 22:46:19 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + 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 ) {
ret = parity_sync ( parity [ l ] ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
2015-02-02 23:39:46 +01:00
ftag ( " parity_error:%u:%s: Sync error \n " , i , lev_config_name ( l ) ) ;
ferr ( " DANGER! Unexpected sync error in %s disk. \n " , lev_name ( l ) ) ;
ferr ( " Ensure that disk '%s' is sane. \n " , lev_config_name ( l ) ) ;
2015-01-31 16:44:46 +01:00
printf ( " Stopping at block %u \n " , i ) ;
+ + error ;
goto bail ;
/* LCOV_EXCL_STOP */
}
}
2014-12-30 09:45:35 +01:00
if ( io_error )
2015-02-02 23:39:46 +01:00
ferr ( " WARNING! Unexpected input/output errors! The failing blocks are now marked as bad! \n " ) ;
2014-12-30 09:45:35 +01:00
if ( silent_error )
2015-02-02 23:39:46 +01:00
ferr ( " WARNING! Unexpected data errors! The failing blocks are now marked as bad! \n " ) ;
2014-12-30 09:45:35 +01:00
if ( io_error | | silent_error ) {
2015-02-02 23:39:46 +01:00
ferr ( " Use 'snapraid status' to list the bad blocks. \n " ) ;
ferr ( " Use 'snapraid -e fix' to recover. \n " ) ;
2014-12-29 19:36:01 +01:00
}
2014-12-27 23:14:06 +01:00
if ( error | | silent_error | | io_error ) {
2013-10-19 13:24:35 +02:00
printf ( " \n " ) ;
2014-12-27 23:14:06 +01:00
printf ( " %8u file errors \n " , error ) ;
printf ( " %8u io errors \n " , io_error ) ;
2013-10-19 13:24:35 +02:00
printf ( " %8u data errors \n " , silent_error ) ;
printf ( " WARNING! There are errors! \n " ) ;
2013-09-21 19:11:11 +02:00
} else {
/* print the result only if processed something */
if ( countpos ! = 0 )
printf ( " Everything OK \n " ) ;
}
2015-02-02 23:39:46 +01:00
ftag ( " summary:error_file:%u \n " , error ) ;
ftag ( " summary:error_io:%u \n " , io_error ) ;
ftag ( " summary:error_data:%u \n " , silent_error ) ;
2014-12-27 23:14:06 +01:00
if ( error + silent_error + io_error = = 0 )
2015-02-02 23:39:46 +01:00
ftag ( " summary:exit:ok \n " ) ;
2013-10-03 22:44:33 +02:00
else
2015-02-02 23:39:46 +01:00
ftag ( " summary:exit:error \n " ) ;
2013-10-03 22:44:33 +02:00
fflush ( stdlog ) ;
2013-09-30 22:24:30 +02:00
2011-03-18 22:38:11 +01:00
bail :
2014-09-27 00:32:39 +02:00
for ( j = 0 ; j < diskmax ; + + j ) {
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-02-02 23:39:46 +01:00
ferr ( " 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 ) ;
2011-04-23 21:38:26 +02:00
free ( buffer_alloc ) ;
free ( buffer ) ;
2013-07-01 23:08:25 +02:00
free ( rehandle_alloc ) ;
2014-01-14 22:10:47 +01:00
free ( failed ) ;
free ( failed_map ) ;
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 ;
2014-11-09 10:25:39 +01:00
struct snapraid_parity_handle parity [ LEV_MAX ] ;
2014-09-17 21:49:37 +02:00
/* the following initialization is to avoid clang warnings about */
/* potential state->level change, that never happens */
2014-11-09 10:25:39 +01:00
struct snapraid_parity_handle * parity_ptr [ LEV_MAX ] = { 0 } ;
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
2012-03-18 10:25:53 +01:00
printf ( " 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-02-02 23:39:46 +01:00
ferr ( " 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 */
parity_ptr [ l ] = & parity [ l ] ;
2014-11-09 10:32:00 +01:00
ret = parity_create ( parity_ptr [ 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-02-02 23:39:46 +01:00
ferr ( " 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-02-02 23:39:46 +01:00
ferr ( " 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-02-02 23:39:46 +01:00
ferr ( " 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-02-02 23:39:46 +01:00
ferr ( " If this happens because you are using an old content file, \n " ) ;
ferr ( " you can 'sync' anyway using 'snapraid --force-full sync' \n " ) ;
ferr ( " to force a full rebuild of the parity. \n " ) ;
2015-02-01 18:48:50 +01:00
} else {
2015-02-02 23:39:46 +01:00
ferr ( " It's possible that the parity disks are not mounted. \n " ) ;
ferr ( " If instead you are adding a new parity level, you can 'sync' using \n " ) ;
ferr ( " '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
2015-02-01 18:48:50 +01:00
/* now change the size of all parities */
for ( l = 0 ; l < state - > level ; + + l ) {
2013-09-15 21:12:41 +02:00
/* 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_ptr [ l ] , size , & out_size , state - > opt . skip_fallocate ) ;
2011-10-09 07:51:25 +02:00
if ( ret = = - 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2012-10-09 23:02:46 +02:00
parity_overflow ( state , out_size ) ;
2015-02-02 23:39:46 +01:00
ferr ( " WARNING! Without an accessible %s file, it isn't possible to sync. \n " , lev_name ( l ) ) ;
2011-04-23 21:38:26 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-04-23 21:38:26 +02:00
}
2013-09-11 23:30:32 +02:00
}
2014-12-07 00:06:53 +01:00
/* after resizing parity files, refresh again the free info */
2014-11-09 19:14:29 +01:00
state_refresh ( state ) ;
2014-02-08 16:49:24 +01:00
unrecoverable_error = 0 ;
2014-02-10 20:53:47 +01:00
if ( state - > opt . prehash ) {
2014-02-02 23:03:37 +01:00
printf ( " Hashing... \n " ) ;
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 ;
/* continue, in case also doing the sync */
/* LCOV_EXCL_STOP */
}
2014-02-02 23:03:37 +01:00
if ( state - > need_write )
state_write ( state ) ;
}
2014-02-08 16:49:24 +01:00
if ( ! skip_sync ) {
printf ( " 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 ) {
ret = state_sync_process ( state , parity_ptr , blockstart , blockmax ) ;
if ( ret = = - 1 ) {
/* LCOV_EXCL_START */
+ + unrecoverable_error ;
/* continue, as we are already exiting */
/* LCOV_EXCL_STOP */
}
} else {
printf ( " 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 ) {
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 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2015-02-02 23:39:46 +01:00
ferr ( " 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