2026-04-02 16:00:27 +02:00
// SPDX-License-Identifier: GPL-3.0-or-later
2026-04-02 16:25:49 +02:00
// Copyright (C) 2011 Andrea Mazzoleni
2011-03-04 20:59:21 +01:00
# 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"
2015-03-02 23:01:15 +01:00
# include "parity.h"
2011-03-04 20:59:21 +01:00
2026-05-03 11:25:24 +02:00
static const char * es ( int err )
{
if ( is_hw ( err ) )
return " error_io " ;
else
return " error " ;
}
2011-03-04 20:59:21 +01:00
struct snapraid_scan {
2014-02-12 20:02:35 +01:00
struct snapraid_state * state ; /**< State used. */
struct snapraid_disk * disk ; /**< Disk used. */
2021-11-28 15:50:38 +01:00
thread_id_t thread ; /**< Thread used for scanning the disk */
int is_diff ; /**< If it's a diff command or a scanning */
int need_write ; /**< If a state write is required */
2011-05-24 21:49:15 +02:00
/**
* Counters of changes .
*/
2013-08-26 20:33:25 +02:00
unsigned count_equal ; /**< Files equal. */
2014-03-02 21:56:59 +01:00
unsigned count_move ; /**< Files with a different name, but equal inode, size and timestamp in the same disk. */
2013-08-26 20:33:25 +02:00
unsigned count_restore ; /**< Files with equal name, size and timestamp, but different inode. */
2015-04-30 22:05:53 +02:00
unsigned count_change ; /**< Files with same name, but different size and/or timestamp. */
unsigned count_copy ; /**< Files new, with same name size and timestamp of a file in a different disk. */
2026-03-14 17:48:30 +01:00
unsigned count_relocate ; /**< Like copy, but with the original disappeared. */
2013-08-26 20:33:25 +02:00
unsigned count_insert ; /**< Files new. */
2015-04-30 22:05:53 +02:00
unsigned count_remove ; /**< Files removed. */
2011-05-24 21:49:15 +02:00
2011-08-26 14:18:38 +02:00
tommy_list file_insert_list ; /**< Files to insert. */
tommy_list link_insert_list ; /**< Links to insert. */
2013-01-27 16:44:04 +01:00
tommy_list dir_insert_list ; /**< Dirs to insert. */
2026-01-10 16:30:35 +01:00
tommy_list local_filter_list ; /**< Filter list specific for the disk. */
2011-09-23 21:48:50 +02:00
/* nodes for data structures */
tommy_node node ;
2011-03-04 20:59:21 +01:00
} ;
2021-11-28 15:50:38 +01:00
static struct snapraid_scan * scan_alloc ( struct snapraid_state * state , struct snapraid_disk * disk , int is_diff )
{
struct snapraid_scan * scan ;
scan = malloc_nofail ( sizeof ( struct snapraid_scan ) ) ;
scan - > state = state ;
scan - > disk = disk ;
scan - > count_equal = 0 ;
scan - > count_move = 0 ;
scan - > count_copy = 0 ;
2026-03-14 17:48:30 +01:00
scan - > count_relocate = 0 ;
2021-11-28 15:50:38 +01:00
scan - > count_restore = 0 ;
scan - > count_change = 0 ;
scan - > count_remove = 0 ;
scan - > count_insert = 0 ;
tommy_list_init ( & scan - > file_insert_list ) ;
tommy_list_init ( & scan - > link_insert_list ) ;
tommy_list_init ( & scan - > dir_insert_list ) ;
2026-01-10 16:30:35 +01:00
tommy_list_init ( & scan - > local_filter_list ) ;
2021-11-28 15:50:38 +01:00
scan - > is_diff = is_diff ;
scan - > need_write = 0 ;
# if HAVE_THREAD
thread_mutex_init ( & disk - > stamp_mutex ) ;
# endif
return scan ;
}
static void scan_free ( struct snapraid_scan * scan )
{
# if HAVE_THREAD
thread_mutex_destroy ( & scan - > disk - > stamp_mutex ) ;
# endif
2026-01-10 16:30:35 +01:00
tommy_list_foreach ( & scan - > local_filter_list , filter_free ) ;
2021-11-28 15:50:38 +01:00
free ( scan ) ;
}
static void stamp_lock ( struct snapraid_disk * disk )
{
# if HAVE_THREAD
thread_mutex_lock ( & disk - > stamp_mutex ) ;
2021-11-29 17:31:52 +01:00
# else
( void ) disk ;
2021-11-28 15:50:38 +01:00
# endif
}
static void stamp_unlock ( struct snapraid_disk * disk )
{
# if HAVE_THREAD
thread_mutex_unlock ( & disk - > stamp_mutex ) ;
2021-11-29 17:31:52 +01:00
# else
( void ) disk ;
2021-11-28 15:50:38 +01:00
# endif
}
2013-01-27 18:58:48 +01:00
/**
2015-10-07 22:07:20 +02:00
* Remove the specified link from the data set .
2013-01-27 18:58:48 +01:00
*/
2015-10-08 22:19:52 +02:00
static void scan_link_remove ( struct snapraid_scan * scan , struct snapraid_link * slink )
2013-01-27 18:58:48 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_disk * disk = scan - > disk ;
2013-01-27 18:58:48 +01:00
/* state changed */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2013-01-27 18:58:48 +01:00
/* remove the file from the link containers */
2015-10-08 22:19:52 +02:00
tommy_hashdyn_remove_existing ( & disk - > linkset , & slink - > nodeset ) ;
tommy_list_remove_existing ( & disk - > linklist , & slink - > nodelist ) ;
2013-01-27 18:58:48 +01:00
/* deallocate */
2015-10-08 22:19:52 +02:00
link_free ( slink ) ;
2013-01-27 18:58:48 +01:00
}
/**
2015-10-07 22:07:20 +02:00
* Insert the specified link in the data set .
2013-01-27 18:58:48 +01:00
*/
2015-10-08 22:19:52 +02:00
static void scan_link_insert ( struct snapraid_scan * scan , struct snapraid_link * slink )
2013-01-27 18:58:48 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_disk * disk = scan - > disk ;
2013-01-27 18:58:48 +01:00
/* state changed */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2013-01-27 18:58:48 +01:00
/* insert the link in the link containers */
2015-10-08 22:19:52 +02:00
tommy_hashdyn_insert ( & disk - > linkset , & slink - > nodeset , slink , link_name_hash ( slink - > sub ) ) ;
tommy_list_insert_tail ( & disk - > linklist , & slink - > nodelist , slink ) ;
2013-01-27 18:58:48 +01:00
}
/**
2015-10-07 22:07:20 +02:00
* Process a symbolic link .
2013-01-27 18:58:48 +01:00
*/
2014-10-25 17:47:22 +02:00
static void scan_link ( struct snapraid_scan * scan , int is_diff , const char * sub , const char * linkto , unsigned link_flag )
2013-01-27 18:58:48 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
2015-10-08 22:19:52 +02:00
struct snapraid_link * slink ;
2016-02-09 23:12:33 +01:00
char esc_buffer [ ESC_MAX ] ;
2013-01-27 18:58:48 +01:00
/* check if the link already exists */
2015-10-08 22:19:52 +02:00
slink = tommy_hashdyn_search ( & disk - > linkset , link_name_compare_to_arg , sub , link_name_hash ( sub ) ) ;
if ( slink ) {
2013-01-27 18:58:48 +01:00
/* check if multiple files have the same name */
2015-10-08 22:19:52 +02:00
if ( link_flag_has ( slink , FILE_IS_PRESENT ) ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency for link '%s%s' \n " , disk - > dir , sub ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-01-27 18:58:48 +01:00
}
/* mark as present */
2015-10-08 22:19:52 +02:00
link_flag_set ( slink , FILE_IS_PRESENT ) ;
2013-01-27 18:58:48 +01:00
/* check if the link is not changed and it's of the same kind */
2015-10-08 22:19:52 +02:00
if ( strcmp ( slink - > linkto , linkto ) = = 0 & & link_flag = = link_flag_get ( slink , FILE_IS_LINK_MASK ) ) {
2013-01-27 18:58:48 +01:00
/* it's equal */
+ + scan - > count_equal ;
2026-01-14 10:35:57 +01:00
if ( state - > opt . gui_verbose ) {
2016-05-29 18:19:13 +02:00
log_tag ( " scan:equal:%s:%s \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) ) ;
2013-01-27 18:58:48 +01:00
}
} else {
/* it's an update */
2013-08-26 20:50:15 +02:00
/* we have to save the linkto/type */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2013-08-26 20:50:15 +02:00
+ + scan - > count_change ;
2016-05-29 18:19:13 +02:00
log_tag ( " scan:update:%s:%s \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) ) ;
2014-10-25 17:47:22 +02:00
if ( is_diff ) {
2021-12-01 18:00:07 +01:00
msg_info ( " update %s \n " , fmt_term ( disk , slink - > sub , esc_buffer ) ) ;
2013-01-27 18:58:48 +01:00
}
/* update it */
2015-10-08 22:19:52 +02:00
free ( slink - > linkto ) ;
slink - > linkto = strdup_nofail ( linkto ) ;
link_flag_let ( slink , link_flag , FILE_IS_LINK_MASK ) ;
2013-01-27 18:58:48 +01:00
}
2013-08-26 20:50:15 +02:00
/* nothing more to do */
return ;
2013-01-27 18:58:48 +01:00
} else {
/* create the new link */
+ + scan - > count_insert ;
2016-05-29 18:19:13 +02:00
log_tag ( " scan:add:%s:%s \n " , disk - > name , esc_tag ( sub , esc_buffer ) ) ;
2014-10-25 17:47:22 +02:00
if ( is_diff ) {
2021-12-01 18:00:07 +01:00
msg_info ( " add %s \n " , fmt_term ( disk , sub , esc_buffer ) ) ;
2013-01-27 18:58:48 +01:00
}
/* and continue to insert it */
}
/* insert it */
2015-10-08 22:19:52 +02:00
slink = link_alloc ( sub , linkto , link_flag ) ;
2013-01-27 18:58:48 +01:00
/* mark it as present */
2015-10-08 22:19:52 +02:00
link_flag_set ( slink , FILE_IS_PRESENT ) ;
2013-01-27 18:58:48 +01:00
/* insert it in the delayed insert list */
2015-10-08 22:19:52 +02:00
tommy_list_insert_tail ( & scan - > link_insert_list , & slink - > nodelist , slink ) ;
2013-01-27 18:58:48 +01:00
}
2011-03-04 20:59:21 +01:00
/**
2015-10-01 18:19:48 +02:00
* Insert the specified file in the parity .
2011-03-04 20:59:21 +01:00
*/
2015-10-01 18:19:48 +02:00
static void scan_file_allocate ( struct snapraid_scan * scan , struct snapraid_file * file )
2011-03-04 20:59:21 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
2015-10-01 18:19:48 +02:00
block_off_t i ;
block_off_t parity_pos ;
/* state changed */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2015-10-01 18:19:48 +02:00
/* allocate the blocks of the file */
parity_pos = disk - > first_free_block ;
for ( i = 0 ; i < file - > blockmax ; + + i ) {
2015-10-04 22:02:05 +02:00
struct snapraid_block * block ;
struct snapraid_block * over_block ;
2015-10-01 18:19:48 +02:00
snapraid_info info ;
/* increment the position until the first really free block */
2016-01-16 14:26:05 +01:00
while ( block_has_file ( fs_par2block_find ( disk , parity_pos ) ) )
2015-10-01 18:19:48 +02:00
+ + parity_pos ;
2015-10-04 22:02:05 +02:00
/* get block we are going to overwrite, if any */
2016-01-16 14:26:05 +01:00
over_block = fs_par2block_find ( disk , parity_pos ) ;
2015-10-04 22:02:05 +02:00
/* deallocate it */
2016-01-16 14:26:05 +01:00
if ( over_block ! = BLOCK_NULL )
2015-10-04 22:02:05 +02:00
fs_deallocate ( disk , parity_pos ) ;
2015-10-01 18:19:48 +02:00
/* get block specific info */
info = info_get ( & state - > infoarr , parity_pos ) ;
2015-10-04 22:02:05 +02:00
/* get the new block we are going to write */
block = fs_file2block_get ( file , i ) ;
2015-10-01 18:19:48 +02:00
/* if the file block already has an updated hash without rehash */
if ( block_has_updated_hash ( block ) & & ! info_get_rehash ( info ) ) {
/* the only possible case is for REP blocks */
assert ( block_state_get ( block ) = = BLOCK_STATE_REP ) ;
/* convert to a REP block */
block_state_set ( block , BLOCK_STATE_REP ) ;
/* and keep the hash as it's */
} else {
2015-10-04 22:02:05 +02:00
unsigned over_state ;
2015-10-01 18:19:48 +02:00
/* convert to a CHG block */
block_state_set ( block , BLOCK_STATE_CHG ) ;
2014-02-12 20:02:35 +01:00
2015-10-01 18:19:48 +02:00
/* state of the block we are going to overwrite */
2015-10-04 22:02:05 +02:00
over_state = block_state_get ( over_block ) ;
2015-10-01 18:19:48 +02:00
/* if the block is an empty one */
2015-10-04 22:02:05 +02:00
if ( over_state = = BLOCK_STATE_EMPTY ) {
2015-10-01 18:19:48 +02:00
/* the block was empty and filled with zeros */
/* set the hash to the special ZERO value */
hash_zero_set ( block - > hash ) ;
} else {
/* otherwise it's a DELETED one */
2015-10-04 22:02:05 +02:00
assert ( over_state = = BLOCK_STATE_DELETED ) ;
2015-10-01 18:19:48 +02:00
/* copy the past hash of the block */
2016-04-07 21:39:02 +02:00
memcpy ( block - > hash , over_block - > hash , BLOCK_HASH_SIZE ) ;
2015-10-01 18:19:48 +02:00
/* if we have not already cleared the past hash */
if ( ! state - > clear_past_hash ) {
/* in this case we don't know if the old state is still the one */
/* stored inside the parity, because after an aborted sync, the parity */
/* may be or may be not have been updated with the new data */
/* Then we reset the hash to a bogus value */
/* For example: */
/* - One file is deleted */
/* - Sync aborted after, updating the parity to the new state, */
/* but without saving the content file representing this new state. */
/* - Another file is added again (exactly here) */
2016-01-02 16:52:20 +01:00
/* with the hash of DELETED block not representing the real parity state */
2015-10-01 18:19:48 +02:00
hash_invalid_set ( block - > hash ) ;
}
}
}
/* store in the disk map, after invalidating all the other blocks */
2015-10-04 22:02:05 +02:00
fs_allocate ( disk , parity_pos , file , i ) ;
2015-10-01 18:19:48 +02:00
/* set the new free position */
disk - > first_free_block = parity_pos + 1 ;
}
/* insert in the list of contained files */
tommy_list_insert_tail ( & disk - > filelist , & file - > nodelist , file ) ;
}
/**
2015-11-20 18:29:53 +01:00
* Delete the specified file from the parity .
*
* Note that the parity remains allocated , but the blocks and the file are marked as DELETED .
* The file is then inserted in the deleted set , and it should not be deallocated ,
* as the parity still references it .
2015-10-01 18:19:48 +02:00
*/
static void scan_file_deallocate ( struct snapraid_scan * scan , struct snapraid_file * file )
{
struct snapraid_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
2011-09-23 21:48:50 +02:00
block_off_t i ;
2011-03-22 23:23:45 +01:00
2015-10-01 18:19:48 +02:00
/* remove from the list of contained files */
tommy_list_remove_existing ( & disk - > filelist , & file - > nodelist ) ;
2011-03-22 23:23:45 +01:00
/* state changed */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2011-03-04 20:59:21 +01:00
2015-11-20 18:29:53 +01:00
/* here we are supposed to adjust the ::first_free_block position */
/* with the parity position we are deleting */
/* but we also know that we do only delayed insert, after all the deletion, */
/* so at this point ::first_free_block is always at 0, and we don't need to update it */
if ( disk - > first_free_block ! = 0 ) {
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency for first free position at '%u' deallocating file '%s' \n " , disk - > first_free_block , file - > sub ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2015-11-20 18:29:53 +01:00
/* LCOV_EXCL_STOP */
}
2011-03-04 20:59:21 +01:00
/* free all the blocks of the file */
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < file - > blockmax ; + + i ) {
2015-10-04 22:02:05 +02:00
struct snapraid_block * block = fs_file2block_get ( file , i ) ;
2013-02-24 19:02:53 +01:00
unsigned block_state ;
2011-03-04 20:59:21 +01:00
2013-02-24 19:02:53 +01:00
/* in case we scan after an aborted sync, */
2014-02-01 23:32:53 +01:00
/* we could get also intermediate states */
2013-02-24 19:02:53 +01:00
block_state = block_state_get ( block ) ;
switch ( block_state ) {
case BLOCK_STATE_BLK :
/* we keep the hash making it an "old" hash, because the parity is still containing data for it */
break ;
2013-10-27 20:59:37 +01:00
case BLOCK_STATE_CHG :
2014-06-13 20:52:03 +02:00
/* if we have not already cleared the past hash */
if ( ! state - > clear_past_hash ) {
2013-09-29 21:01:31 +02:00
/* in these cases we don't know if the old state is still the one */
/* stored inside the parity, because after an aborted sync, the parity */
2014-02-01 17:57:26 +01:00
/* may be or may be not have been updated with the data that it's now */
/* deleted. Then we reset the hash to a bogus value. */
/* For example: */
/* - One file is added */
/* - Sync aborted after updating the parity to the new state, */
/* but without saving the content file representing this new state. */
/* - File is now deleted after the aborted sync */
/* - Sync again, deleting the blocks (exactly here) */
2016-01-02 16:52:20 +01:00
/* with the hash of CHG block not representing the real parity state */
2015-10-01 23:22:21 +02:00
hash_invalid_set ( block - > hash ) ;
2013-09-29 21:01:31 +02:00
}
2013-02-24 19:02:53 +01:00
break ;
2014-02-01 17:57:26 +01:00
case BLOCK_STATE_REP :
/* we just don't know the old hash, and then we set it to invalid */
2015-10-01 23:22:21 +02:00
hash_invalid_set ( block - > hash ) ;
2014-02-01 17:57:26 +01:00
break ;
2014-09-27 00:32:39 +02:00
default :
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency in file '%s' deallocating block '%u:%u' state %u \n " , file - > sub , i , file - > blockmax , block_state ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-02-24 19:02:53 +01:00
}
2015-10-01 23:22:21 +02:00
/* set the block as deleted */
block_state_set ( block , BLOCK_STATE_DELETED ) ;
2011-03-04 20:59:21 +01:00
}
2015-10-01 18:19:48 +02:00
2015-10-05 21:40:44 +02:00
/* mark the file as deleted */
file_flag_set ( file , FILE_IS_DELETED ) ;
2015-10-01 23:22:21 +02:00
/* insert it in the list of deleted blocks */
tommy_list_insert_tail ( & disk - > deletedlist , & file - > nodelist , file ) ;
2015-10-01 18:19:48 +02:00
}
static void scan_file_delayed_allocate ( struct snapraid_scan * scan , struct snapraid_file * file )
{
2026-05-03 11:25:24 +02:00
char esc_buffer [ ESC_MAX ] ;
2015-10-01 18:19:48 +02:00
struct snapraid_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
/* if we sort for physical offsets we have to read them for new files */
if ( state - > opt . force_order = = SORT_PHYSICAL
& & file - > physical = = FILEPHY_UNREAD_OFFSET
) {
char path_next [ PATH_MAX ] ;
pathprint ( path_next , sizeof ( path_next ) , " %s%s " , disk - > dir , file - > sub ) ;
if ( filephy ( path_next , file - > size , & file - > physical ) ! = 0 ) {
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: File physycal offset error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( file - > sub , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error in getting the physical offset of file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2015-10-01 18:19:48 +02:00
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
}
/* insert in the delayed list */
tommy_list_insert_tail ( & scan - > file_insert_list , & file - > nodelist , file ) ;
2013-12-24 21:46:27 +01:00
}
/**
2015-10-07 22:07:20 +02:00
* Check if a file is completely formed of blocks with invalid parity ,
2014-02-16 08:59:59 +01:00
* and no rehash is tagged , and if it has at least one block .
2013-12-24 21:46:27 +01:00
*/
2015-10-01 18:19:48 +02:00
static int file_is_full_invalid_parity_and_stable ( struct snapraid_state * state , struct snapraid_disk * disk , struct snapraid_file * file )
2013-12-24 21:46:27 +01:00
{
block_off_t i ;
2014-02-16 08:59:59 +01:00
/* with no block, it never has an invalid parity */
2013-12-24 21:46:27 +01:00
if ( file - > blockmax = = 0 )
return 0 ;
/* check all blocks */
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < file - > blockmax ; + + i ) {
2014-06-15 17:56:43 +02:00
snapraid_info info ;
2015-10-04 22:02:05 +02:00
struct snapraid_block * block = fs_file2block_get ( file , i ) ;
2015-10-08 18:41:01 +02:00
block_off_t parity_pos ;
2013-12-24 21:46:27 +01:00
2014-02-16 08:59:59 +01:00
/* exclude blocks with parity */
2014-02-01 23:32:53 +01:00
if ( ! block_has_invalid_parity ( block ) )
2013-12-24 21:46:27 +01:00
return 0 ;
2014-02-16 08:59:59 +01:00
2015-10-08 18:41:01 +02:00
/*
2016-01-15 19:50:21 +01:00
* Get the parity position .
*
* Note that here we expect to always have mapped
* parity , because kept files always have it .
*
2016-01-16 14:26:05 +01:00
* Anyway , checking for POS_NULL doesn ' t hurt .
2015-10-08 18:41:01 +02:00
*/
2016-01-16 14:26:05 +01:00
parity_pos = fs_file2par_find ( disk , file , i ) ;
2015-10-08 18:41:01 +02:00
2016-01-15 19:50:21 +01:00
/* if it's not mapped, it cannot have rehash */
2016-01-16 14:26:05 +01:00
if ( parity_pos ! = POS_NULL ) {
2016-01-15 19:50:21 +01:00
/* get block specific info */
info = info_get ( & state - > infoarr , parity_pos ) ;
2014-02-16 08:59:59 +01:00
2016-01-15 19:50:21 +01:00
/* if rehash fails */
if ( info_get_rehash ( info ) )
return 0 ;
}
2013-12-24 21:46:27 +01:00
}
return 1 ;
}
2014-02-12 20:02:35 +01:00
/**
2015-10-07 22:07:20 +02:00
* Check if a file is completely formed of blocks with an updated hash ,
2014-02-16 08:59:59 +01:00
* and no rehash is tagged , and if it has at least one block .
2014-02-12 20:02:35 +01:00
*/
2015-10-01 18:19:48 +02:00
static int file_is_full_hashed_and_stable ( struct snapraid_state * state , struct snapraid_disk * disk , struct snapraid_file * file )
2014-02-12 20:02:35 +01:00
{
block_off_t i ;
2014-02-16 08:59:59 +01:00
/* with no block, it never has a hash */
2014-02-12 20:02:35 +01:00
if ( file - > blockmax = = 0 )
return 0 ;
/* check all blocks */
2014-09-27 00:32:39 +02:00
for ( i = 0 ; i < file - > blockmax ; + + i ) {
2014-02-12 20:02:35 +01:00
snapraid_info info ;
2015-10-04 22:02:05 +02:00
struct snapraid_block * block = fs_file2block_get ( file , i ) ;
2015-10-07 22:44:39 +02:00
block_off_t parity_pos ;
2014-02-12 20:02:35 +01:00
2014-02-16 08:59:59 +01:00
/* exclude blocks without hash */
2014-02-12 20:02:35 +01:00
if ( ! block_has_updated_hash ( block ) )
return 0 ;
2015-10-07 22:44:39 +02:00
/*
2016-01-15 19:50:21 +01:00
* Get the parity position .
2015-10-07 22:44:39 +02:00
*
2016-01-15 19:50:21 +01:00
* Note that it ' s possible to have files
* not mapped into the parity , even if they
* have a valid hash .
*
* This happens for example , for ' copied ' files
* that have REP blocks , but not yet mapped .
*
* If there are multiple copies , it ' s also possible
* that such files are used as ' source ' to copy
* hashes , and then to get them inside this function .
2015-10-07 22:44:39 +02:00
*/
2016-01-16 14:26:05 +01:00
parity_pos = fs_file2par_find ( disk , file , i ) ;
2015-10-07 22:44:39 +02:00
2016-01-15 19:50:21 +01:00
/* if it's not mapped, it cannot have rehash */
2016-01-16 14:26:05 +01:00
if ( parity_pos ! = POS_NULL ) {
2016-01-15 19:50:21 +01:00
/* get block specific info */
info = info_get ( & state - > infoarr , parity_pos ) ;
2014-02-12 20:02:35 +01:00
2016-01-15 19:50:21 +01:00
/* exclude blocks needing a rehash */
if ( info_get_rehash ( info ) )
return 0 ;
}
2014-02-12 20:02:35 +01:00
}
return 1 ;
}
2014-10-04 10:27:23 +02:00
/**
* Refresh the file info .
*
* This is needed by Windows as the normal way to list directories may report not
* updated info . Only the GetFileInformationByHandle ( ) func , called file - by - file ,
2015-01-10 10:21:05 +01:00
* really ensures to return synced info .
2014-10-14 22:47:21 +02:00
*
* If this happens , we read also the physical offset , to avoid to read it later .
2014-10-04 10:27:23 +02:00
*/
2014-10-14 22:47:21 +02:00
static void scan_file_refresh ( struct snapraid_scan * scan , const char * sub , struct stat * st , uint64_t * physical )
2014-10-04 10:27:23 +02:00
{
# if HAVE_LSTAT_SYNC
2026-05-03 11:25:24 +02:00
char esc_buffer [ ESC_MAX ] ;
2014-10-14 22:47:21 +02:00
struct snapraid_state * state = scan - > state ;
2014-10-04 10:27:23 +02:00
struct snapraid_disk * disk = scan - > disk ;
/* if the st_sync is not set, ensure to get synced info */
if ( st - > st_sync = = 0 ) {
char path_next [ PATH_MAX ] ;
struct stat synced_st ;
pathprint ( path_next , sizeof ( path_next ) , " %s%s " , disk - > dir , sub ) ;
2014-10-14 22:47:21 +02:00
/* if we sort for physical offsets we have to read them for new files */
if ( state - > opt . force_order = = SORT_PHYSICAL
& & * physical = = FILEPHY_UNREAD_OFFSET
) {
/* do nothing, leave the pointer to read the physical offset */
} else {
physical = 0 ; /* set the pointer to 0 to read nothing */
}
if ( lstat_sync ( path_next , & synced_st , physical ) ! = 0 ) {
2014-10-04 10:27:23 +02:00
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Stat error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error in stat file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2014-10-04 10:27:23 +02:00
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
if ( st - > st_mtime ! = synced_st . st_mtime
| | st - > st_mtimensec ! = synced_st . st_mtimensec
) {
2014-10-14 23:01:01 +02:00
# ifndef _WIN32
/*
* In Windows having different metadata is expected with open files
* because the metadata in the directory is updated only when the file
* is closed .
*
2020-05-02 14:21:40 +02:00
* The same happens for hardlinks that duplicate metadata .
2017-10-29 18:07:01 +01:00
* The link metadata is updated only when the link is opened .
* This extends also to st_size and st_nlink .
*
2014-10-14 23:01:01 +02:00
* See also :
* Why is the file size reported incorrectly for files that are still being written to ?
* http : //blogs.msdn.com/b/oldnewthing/archive/2011/12/26/10251026.aspx
*/
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Uncached time change error. \n " , es ( ESOFT ) , 0 , disk - > name , esc_tag ( sub , esc_buffer ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Detected uncached time change from % " PRIu64 " .%09u to % " PRIu64 " .%09u for file '%s' \n " ,
2017-10-29 18:07:01 +01:00
( uint64_t ) st - > st_mtime , ( uint32_t ) st - > st_mtimensec , ( uint64_t ) synced_st . st_mtime , ( uint32_t ) synced_st . st_mtimensec , sub ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " It's better if you run SnapRAID without other processes running. \n " ) ;
2014-10-14 23:01:01 +02:00
# endif
2014-10-04 10:27:23 +02:00
st - > st_mtime = synced_st . st_mtime ;
st - > st_mtimensec = synced_st . st_mtimensec ;
}
if ( st - > st_size ! = synced_st . st_size ) {
2014-10-14 23:01:01 +02:00
# ifndef _WIN32
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Uncached size change error. \n " , es ( ESOFT ) , 0 , disk - > name , esc_tag ( sub , esc_buffer ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Detected uncached size change from % " PRIu64 " to % " PRIu64 " for file '%s' \n " ,
2017-10-29 18:07:01 +01:00
( uint64_t ) st - > st_size , ( uint64_t ) synced_st . st_size , sub ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " It's better if you run SnapRAID without other processes running. \n " ) ;
2014-10-14 23:01:01 +02:00
# endif
2014-10-04 10:27:23 +02:00
st - > st_size = synced_st . st_size ;
}
2017-10-29 18:07:01 +01:00
if ( st - > st_nlink ! = synced_st . st_nlink ) {
# ifndef _WIN32
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Uncached nlink change error. \n " , es ( ESOFT ) , 0 , disk - > name , esc_tag ( sub , esc_buffer ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Detected uncached nlink change from %u to %u for file '%s' \n " ,
2017-11-03 23:46:45 +01:00
( uint32_t ) st - > st_nlink , ( uint32_t ) synced_st . st_nlink , sub ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " It's better if you run SnapRAID without other processes running. \n " ) ;
2017-10-29 18:07:01 +01:00
# endif
st - > st_nlink = synced_st . st_nlink ;
}
2014-10-04 10:27:23 +02:00
if ( st - > st_ino ! = synced_st . st_ino ) {
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Uncached inode change error. \n " , es ( ESOFT ) , 0 , disk - > name , esc_tag ( sub , esc_buffer ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " DANGER! Detected uncached inode change from % " PRIu64 " to % " PRIu64 " for file '%s' \n " ,
2017-10-29 18:07:01 +01:00
( uint64_t ) st - > st_ino , ( uint64_t ) synced_st . st_ino , sub ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " It's better if you run SnapRAID without other processes running. \n " ) ;
2014-10-04 10:27:23 +02:00
/* at this point, it's too late to change inode */
/* and having inconsistent inodes may result to internal failures */
/* so, it's better to abort */
exit ( EXIT_FAILURE ) ;
}
}
# else
( void ) scan ;
( void ) sub ;
( void ) st ;
2014-10-18 16:52:28 +02:00
( void ) physical ;
2014-10-04 10:27:23 +02:00
# endif
}
2013-12-24 21:46:27 +01:00
/**
2015-10-01 18:19:48 +02:00
* Insert the file in the data set .
2013-12-24 21:46:27 +01:00
*/
2015-10-01 18:19:48 +02:00
static void scan_file_insert ( struct snapraid_scan * scan , struct snapraid_file * file )
2013-12-24 21:46:27 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_disk * disk = scan - > disk ;
2015-10-01 18:19:48 +02:00
/* insert the file in the containers */
if ( ! file_flag_has ( file , FILE_IS_WITHOUT_INODE ) )
tommy_hashdyn_insert ( & disk - > inodeset , & file - > nodeset , file , file_inode_hash ( file - > inode ) ) ;
2021-11-28 15:50:38 +01:00
stamp_lock ( disk ) ;
2015-10-01 18:19:48 +02:00
tommy_hashdyn_insert ( & disk - > pathset , & file - > pathset , file , file_path_hash ( file - > sub ) ) ;
tommy_hashdyn_insert ( & disk - > stampset , & file - > stampset , file , file_stamp_hash ( file - > size , file - > mtime_sec , file - > mtime_nsec ) ) ;
2021-11-28 15:50:38 +01:00
stamp_unlock ( disk ) ;
2013-12-24 21:46:27 +01:00
2015-10-01 23:22:21 +02:00
/* delayed allocation of the parity */
2015-10-01 18:19:48 +02:00
scan_file_delayed_allocate ( scan , file ) ;
2013-12-24 21:46:27 +01:00
}
/**
2015-10-01 18:19:48 +02:00
* Remove the file from the data set .
*
* File is then deleted .
2013-12-24 21:46:27 +01:00
*/
2014-02-12 20:02:35 +01:00
static void scan_file_remove ( struct snapraid_scan * scan , struct snapraid_file * file )
2013-12-24 21:46:27 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_disk * disk = scan - > disk ;
2015-10-01 18:19:48 +02:00
/* remove the file from the containers */
if ( ! file_flag_has ( file , FILE_IS_WITHOUT_INODE ) )
2013-08-26 20:33:25 +02:00
tommy_hashdyn_remove_existing ( & disk - > inodeset , & file - > nodeset ) ;
2011-10-09 23:41:08 +02:00
tommy_hashdyn_remove_existing ( & disk - > pathset , & file - > pathset ) ;
2021-11-28 15:50:38 +01:00
stamp_lock ( disk ) ;
2014-02-12 20:02:35 +01:00
tommy_hashdyn_remove_existing ( & disk - > stampset , & file - > stampset ) ;
2021-11-28 15:50:38 +01:00
stamp_unlock ( disk ) ;
2011-03-04 20:59:21 +01:00
2015-10-01 18:19:48 +02:00
/* deallocate the file from the parity */
scan_file_deallocate ( scan , file ) ;
2011-03-04 20:59:21 +01:00
}
/**
2015-10-01 18:19:48 +02:00
* Keep the file as it ' s ( or with only a name / inode modification ) .
*
* If the file is kept , nothing has to be done .
*
* But if a file contains only blocks with invalid parity , it ' s reallocated to ensure
* to always minimize the space used in the parity .
*
* This could happen after a failed sync , when some other files are deleted ,
* and then new ones can be moved backward to fill the hole created .
2011-03-04 20:59:21 +01:00
*/
2015-10-01 18:19:48 +02:00
static void scan_file_keep ( struct snapraid_scan * scan , struct snapraid_file * file )
2011-03-04 20:59:21 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_disk * disk = scan - > disk ;
2015-10-01 18:19:48 +02:00
/* if the file is full invalid, schedule a reinsert at later stage */
if ( file_is_full_invalid_parity_and_stable ( scan - > state , disk , file ) ) {
struct snapraid_file * copy = file_dup ( file ) ;
2012-02-09 23:17:36 +01:00
2015-10-01 18:19:48 +02:00
/* remove the file */
scan_file_remove ( scan , file ) ;
2014-02-01 23:32:53 +01:00
2015-10-01 18:19:48 +02:00
/* reinsert the copy in the delayed list */
scan_file_insert ( scan , copy ) ;
2011-03-04 20:59:21 +01:00
}
}
2011-03-16 23:52:10 +01:00
/**
2015-10-07 22:07:20 +02:00
* Process a file .
2011-03-16 23:52:10 +01:00
*/
2014-10-25 17:47:22 +02:00
static void scan_file ( struct snapraid_scan * scan , int is_diff , const char * sub , struct stat * st , uint64_t physical )
2011-03-04 20:59:21 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
2011-03-04 20:59:21 +01:00
struct snapraid_file * file ;
2014-02-12 20:02:35 +01:00
tommy_node * i ;
2014-10-04 10:27:23 +02:00
int is_original_file_size_different_than_zero ;
2014-12-09 21:25:17 +01:00
int is_file_already_present ;
2016-01-18 19:05:46 +01:00
data_off_t file_already_present_size ;
int64_t file_already_present_mtime_sec ;
int file_already_present_mtime_nsec ;
2014-12-09 21:25:17 +01:00
int is_file_reported ;
2016-02-09 23:12:33 +01:00
char esc_buffer [ ESC_MAX ] ;
char esc_buffer_alt [ ESC_MAX ] ;
2013-08-27 23:02:12 +02:00
/*
2013-12-14 17:54:04 +01:00
* If the disk has persistent inodes and UUID , try a search on the past inodes ,
2013-09-25 23:20:07 +02:00
* to detect moved files .
2013-08-27 23:02:12 +02:00
*
2016-01-02 16:52:20 +01:00
* For persistent inodes we mean inodes that keep their values when the file - system
2013-09-25 23:20:07 +02:00
* is unmounted and remounted . This don ' t always happen .
2013-08-27 23:02:12 +02:00
*
* Cases found are :
* - Linux FUSE with exFAT driver from https : //code.google.com/p/exfat/.
* Inodes are reassigned at every mount restarting from 1 and incrementing .
2016-01-18 19:38:42 +01:00
* As worse , the exFAT support in FUSE doesn ' t use sub - second precision in timestamps
2013-08-27 23:02:12 +02:00
* making inode collision more easy ( exFAT by design supports 10 ms precision ) .
* - Linux VFAT kernel ( 3.2 ) driver . Inodes are fully reassigned at every mount .
*
2013-09-25 23:20:07 +02:00
* In such cases , to avoid possible random collisions , it ' s better to disable the moved
2013-09-29 21:01:31 +02:00
* file recognition .
2013-09-25 23:20:07 +02:00
*
2013-12-14 17:54:04 +01:00
* For persistent UUID we mean that it has the same UUID as before .
2016-01-02 16:52:20 +01:00
* Otherwise , if the UUID is changed , likely it ' s a new recreated file - system ,
2013-12-14 17:54:04 +01:00
* and then the inode have no meaning .
*
2016-01-02 16:52:20 +01:00
* Note that to disable the search by past inode , we do this implicitly
2013-12-14 17:54:04 +01:00
* removing all the past inode before searching for files .
* This ensures that no file is found with a past inode , but at the same time ,
* it allows to find new files with the same inode , to identify them as hardlinks .
2013-08-27 23:02:12 +02:00
*/
2014-04-03 23:02:43 +02:00
int has_past_inodes = ! disk - > has_volatile_inodes & & ! disk - > has_different_uuid & & ! disk - > has_unsupported_uuid ;
2013-08-27 23:02:12 +02:00
2013-12-14 17:54:04 +01:00
/* always search with the new inode, in the all new inodes found until now, */
/* with the eventual presence of also the past inodes */
uint64_t inode = st - > st_ino ;
2014-09-27 00:32:39 +02:00
2013-09-25 23:20:07 +02:00
file = tommy_hashdyn_search ( & disk - > inodeset , file_inode_compare_to_arg , & inode , file_inode_hash ( inode ) ) ;
2013-12-14 17:54:04 +01:00
/* identify moved files with past inodes and hardlinks with the new inodes */
2011-03-04 20:59:21 +01:00
if ( file ) {
2011-03-20 22:36:49 +01:00
/* check if the file is not changed */
2012-07-13 20:53:57 +02:00
if ( file - > size = = st - > st_size
& & file - > mtime_sec = = st - > st_mtime
2013-08-26 20:33:25 +02:00
& & ( file - > mtime_nsec = = STAT_NSEC ( st )
2014-09-27 00:32:39 +02:00
/* always accept the stored value if it's STAT_NSEC_INVALID */
/* it happens when upgrading from an old version of SnapRAID */
/* not yet supporting the nanosecond field */
| | file - > mtime_nsec = = STAT_NSEC_INVALID
2013-08-26 20:33:25 +02:00
)
2012-07-13 20:53:57 +02:00
) {
2013-08-26 20:33:25 +02:00
/* check if multiple files have the same inode */
if ( file_flag_has ( file , FILE_IS_PRESENT ) ) {
2017-10-29 18:07:01 +01:00
/* if has_volatile_hardlinks is true, the nlink value is not reliable */
if ( ! disk - > has_volatile_hardlinks & & st - > st_nlink = = 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inode '% " PRIu64 " ' inconsistency for file '%s%s' already present \n " , ( uint64_t ) st - > st_ino , disk - > dir , sub ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-08-26 20:33:25 +02:00
}
2017-10-29 18:07:01 +01:00
2014-09-30 21:54:49 +02:00
/* it's a hardlink */
2014-10-25 17:47:22 +02:00
scan_link ( scan , is_diff , sub , file - > sub , FILE_IS_HARDLINK ) ;
2014-09-30 21:54:49 +02:00
return ;
2013-08-26 20:33:25 +02:00
}
2011-03-04 20:59:21 +01:00
/* mark as present */
2011-04-28 23:09:19 +02:00
file_flag_set ( file , FILE_IS_PRESENT ) ;
2011-03-29 23:55:47 +02:00
2014-05-15 23:45:51 +02:00
/* update the nanoseconds mtime only if different */
2013-08-26 20:33:25 +02:00
/* to avoid unneeded updates */
2013-08-18 18:02:25 +02:00
if ( file - > mtime_nsec = = STAT_NSEC_INVALID
2014-05-15 23:45:51 +02:00
& & STAT_NSEC ( st ) ! = file - > mtime_nsec
2012-09-19 21:52:15 +02:00
) {
file - > mtime_nsec = STAT_NSEC ( st ) ;
2012-07-13 20:53:57 +02:00
/* we have to save the new mtime */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2012-07-13 20:53:57 +02:00
}
2011-03-29 23:55:47 +02:00
if ( strcmp ( file - > sub , sub ) ! = 0 ) {
2011-10-09 23:41:08 +02:00
/* if the path is different, it means a moved file with the same inode */
2013-08-26 20:33:25 +02:00
+ + scan - > count_move ;
2011-10-09 23:41:08 +02:00
2016-05-29 18:19:13 +02:00
log_tag ( " scan:move:%s:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) , esc_tag ( sub , esc_buffer_alt ) ) ;
2014-10-25 17:47:22 +02:00
if ( is_diff ) {
2021-12-01 18:00:07 +01:00
msg_info ( " move %s -> %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) , fmt_term ( disk , sub , esc_buffer_alt ) ) ;
2011-06-20 20:35:04 +02:00
}
2013-08-26 20:33:25 +02:00
/* remove from the name set */
2011-10-09 23:41:08 +02:00
tommy_hashdyn_remove_existing ( & disk - > pathset , & file - > pathset ) ;
2011-03-29 23:55:47 +02:00
/* save the new name */
2013-02-26 21:26:45 +01:00
file_rename ( file , sub ) ;
2011-03-29 23:55:47 +02:00
2013-08-26 20:33:25 +02:00
/* reinsert in the name set */
2011-10-09 23:41:08 +02:00
tommy_hashdyn_insert ( & disk - > pathset , & file - > pathset , file , file_path_hash ( file - > sub ) ) ;
/* we have to save the new name */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2011-03-29 23:55:47 +02:00
} else {
/* otherwise it's equal */
+ + scan - > count_equal ;
2011-06-20 20:35:04 +02:00
2026-01-14 10:35:57 +01:00
if ( state - > opt . gui_verbose ) {
2016-05-29 18:19:13 +02:00
log_tag ( " scan:equal:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2011-06-20 20:35:04 +02:00
}
2011-03-29 23:55:47 +02:00
}
2011-05-24 21:49:15 +02:00
2013-12-24 21:46:27 +01:00
/* mark the file as kept */
2014-02-12 20:02:35 +01:00
scan_file_keep ( scan , file ) ;
2013-12-24 21:46:27 +01:00
2011-05-24 21:49:15 +02:00
/* nothing more to do */
2011-03-04 20:59:21 +01:00
return ;
2013-08-26 20:33:25 +02:00
}
2017-10-27 22:56:00 +02:00
/*
* Here the file matches the inode , but not the other info
*
* It could be a modified file with the same name ,
* or a restored / copied file that get assigned a previously used inode ,
* or a file - system with not persistent inodes .
*
* In NTFS it could be also a hardlink , because in NTFS
* hardlink don ' t share the same directory information ,
* like attribute and time .
*
* For example :
* C : > echo A > A
* C : > mklink / H B A
* . . . wait one minute
* C : > echo AAAAAAAAAAAAAA > A
* C : > dir
* . . . both time and size of A and B don ' t match !
*/
2013-08-26 20:33:25 +02:00
if ( file_flag_has ( file , FILE_IS_PRESENT ) ) {
2017-10-29 18:07:01 +01:00
/* if has_volatile_hardlinks is true, the nlink value is not reliable */
if ( ! disk - > has_volatile_hardlinks & & st - > st_nlink = = 1 ) {
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inode '% " PRIu64 " ' inconsistency for files '%s%s' and '%s%s' with same inode but different attributes: size % " PRIu64 " ?% " PRIu64 " , sec % " PRIu64 " ?% " PRIu64 " , nsec %d?%d \n " ,
2017-10-29 18:07:01 +01:00
file - > inode , disk - > dir , sub , disk - > dir , file - > sub ,
file - > size , ( uint64_t ) st - > st_size ,
file - > mtime_sec , ( uint64_t ) st - > st_mtime ,
file - > mtime_nsec , STAT_NSEC ( st ) ) ;
os_abort ( ) ;
/* LCOV_EXCL_STOP */
}
/* LCOV_EXCL_START */
/* suppose it's hardlink with not synced metadata */
2017-10-27 22:56:00 +02:00
scan_link ( scan , is_diff , sub , file - > sub , FILE_IS_HARDLINK ) ;
return ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-08-26 20:33:25 +02:00
}
/* assume a previously used inode, it's the worst case */
2013-08-26 21:02:20 +02:00
/* and we handle it removing the duplicate stored inode. */
2016-01-18 19:05:46 +01:00
/* If the file is found by name later, it will have the inode restored, */
/* otherwise, it will get removed */
2013-08-26 20:33:25 +02:00
/* remove from the inode set */
tommy_hashdyn_remove_existing ( & disk - > inodeset , & file - > nodeset ) ;
2013-08-26 21:02:20 +02:00
/* clear the inode */
/* this is not really needed for correct functionality */
/* because we are going to set FILE_IS_WITHOUT_INODE */
/* but it's easier for debugging to have invalid inodes set to 0 */
file - > inode = 0 ;
2013-08-26 20:33:25 +02:00
/* mark as missing inode */
file_flag_set ( file , FILE_IS_WITHOUT_INODE ) ;
/* go further to find it by name */
}
2014-12-09 21:25:17 +01:00
/* initialize for later overwrite */
is_file_reported = 0 ;
2014-10-04 10:27:23 +02:00
is_original_file_size_different_than_zero = 0 ;
2014-03-02 21:56:59 +01:00
2014-06-05 23:06:13 +02:00
/* then try finding it by name */
2014-02-12 20:02:35 +01:00
file = tommy_hashdyn_search ( & disk - > pathset , file_path_compare_to_arg , sub , file_path_hash ( sub ) ) ;
2014-12-09 21:25:17 +01:00
/* keep track if the file already exists */
is_file_already_present = file ! = 0 ;
if ( is_file_already_present ) {
2013-08-26 20:33:25 +02:00
/* if the file is without an inode */
if ( file_flag_has ( file , FILE_IS_WITHOUT_INODE ) ) {
/* set it now */
file - > inode = st - > st_ino ;
/* insert in the set */
tommy_hashdyn_insert ( & disk - > inodeset , & file - > nodeset , file , file_inode_hash ( file - > inode ) ) ;
/* unmark as missing inode */
file_flag_clear ( file , FILE_IS_WITHOUT_INODE ) ;
2011-03-04 20:59:21 +01:00
} else {
2013-08-26 20:33:25 +02:00
/* here the inode has to be different, otherwise we would have found it before */
if ( file - > inode = = st - > st_ino ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency in inode '% " PRIu64 " ' for files '%s%s' as unexpected matching \n " , file - > inode , disk - > dir , sub ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-03-17 09:54:02 +01:00
}
2013-08-26 20:33:25 +02:00
}
2011-03-18 22:38:11 +01:00
2013-08-26 20:33:25 +02:00
/* for sure it cannot be already present */
if ( file_flag_has ( file , FILE_IS_PRESENT ) ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency in path for file '%s%s' matching and already present \n " , disk - > dir , sub ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-08-26 20:33:25 +02:00
}
2011-07-27 18:51:07 +02:00
2013-08-26 20:33:25 +02:00
/* check if the file is not changed */
if ( file - > size = = st - > st_size
& & file - > mtime_sec = = st - > st_mtime
& & ( file - > mtime_nsec = = STAT_NSEC ( st )
2014-09-27 00:32:39 +02:00
/* always accept the stored value if it's STAT_NSEC_INVALID */
/* it happens when upgrading from an old version of SnapRAID */
/* not yet supporting the nanosecond field */
| | file - > mtime_nsec = = STAT_NSEC_INVALID
2013-08-26 20:33:25 +02:00
)
) {
/* mark as present */
file_flag_set ( file , FILE_IS_PRESENT ) ;
2013-09-30 22:42:33 +02:00
/* update the nano seconds mtime only if different */
/* to avoid unneeded updates */
if ( file - > mtime_nsec = = STAT_NSEC_INVALID
& & STAT_NSEC ( st ) ! = STAT_NSEC_INVALID
) {
file - > mtime_nsec = STAT_NSEC ( st ) ;
2013-08-26 20:33:25 +02:00
2013-09-30 22:42:33 +02:00
/* we have to save the new mtime */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2013-08-26 20:33:25 +02:00
}
2013-12-14 17:54:04 +01:00
/* if when processing the disk we used the past inodes values */
if ( has_past_inodes ) {
2013-10-01 18:22:14 +02:00
/* if persistent inodes are supported, we are sure that the inode number */
/* is now different, because otherwise the file would have been found */
/* when searching by inode. */
2013-09-30 22:42:33 +02:00
/* if the inode is different, it means a rewritten file with the same path */
/* like when restoring a backup that restores also the timestamp */
+ + scan - > count_restore ;
2013-08-26 20:33:25 +02:00
2016-05-29 18:19:13 +02:00
log_tag ( " scan:restore:%s:%s \n " , disk - > name , esc_tag ( sub , esc_buffer ) ) ;
2014-10-25 17:47:22 +02:00
if ( is_diff ) {
2021-12-01 18:00:07 +01:00
msg_info ( " restore %s \n " , fmt_term ( disk , sub , esc_buffer ) ) ;
2013-09-30 22:42:33 +02:00
}
2013-08-26 20:33:25 +02:00
2013-09-30 22:42:33 +02:00
/* remove from the inode set */
tommy_hashdyn_remove_existing ( & disk - > inodeset , & file - > nodeset ) ;
/* save the new inode */
file - > inode = st - > st_ino ;
/* reinsert in the inode set */
tommy_hashdyn_insert ( & disk - > inodeset , & file - > nodeset , file , file_inode_hash ( file - > inode ) ) ;
/* we have to save the new inode */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2013-09-30 22:42:33 +02:00
} else {
2013-10-01 18:22:14 +02:00
/* otherwise it's the case of not persistent inode, where doesn't */
/* matter if the inode is different or equal, because they have no */
/* meaning, and then we don't even save them */
2013-09-30 22:42:33 +02:00
+ + scan - > count_equal ;
2026-01-14 10:35:57 +01:00
if ( state - > opt . gui_verbose ) {
2016-05-29 18:19:13 +02:00
log_tag ( " scan:equal:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
2013-09-30 22:42:33 +02:00
}
}
2013-08-26 20:33:25 +02:00
2013-12-24 21:46:27 +01:00
/* mark the file as kept */
2014-02-12 20:02:35 +01:00
scan_file_keep ( scan , file ) ;
2013-12-24 21:46:27 +01:00
2013-08-26 20:33:25 +02:00
/* nothing more to do */
return ;
}
/* here if the file is changed but with the correct name */
2014-09-27 00:32:39 +02:00
2016-01-18 19:05:46 +01:00
/* save the info for later printout */
file_already_present_size = file - > size ;
file_already_present_mtime_sec = file - > mtime_sec ;
file_already_present_mtime_nsec = file - > mtime_nsec ;
2014-10-04 10:27:23 +02:00
/* keep track if the original file was not of zero size */
is_original_file_size_different_than_zero = file - > size ! = 0 ;
2011-06-20 20:35:04 +02:00
2014-10-04 10:27:23 +02:00
/* remove it, and continue to insert it again */
2014-02-12 20:02:35 +01:00
scan_file_remove ( scan , file ) ;
2013-08-26 20:33:25 +02:00
2014-12-09 21:25:17 +01:00
/* and continue to insert it again */
2016-01-18 22:39:39 +01:00
} else {
file_already_present_size = 0 ;
file_already_present_mtime_sec = 0 ;
file_already_present_mtime_nsec = 0 ;
2011-03-04 20:59:21 +01:00
}
2014-10-04 10:27:23 +02:00
/* refresh the info, to ensure that they are synced, */
2014-12-09 21:25:17 +01:00
/* note that we refresh only the info of the new or modified files */
2014-10-04 10:27:23 +02:00
/* because this is slow operation */
2014-10-14 22:47:21 +02:00
scan_file_refresh ( scan , sub , st , & physical ) ;
2014-10-04 10:27:23 +02:00
2015-11-14 17:03:26 +01:00
# ifndef _WIN32
2014-10-04 10:27:23 +02:00
/* do a safety check to ensure that the common ext4 case of zeroing */
/* the size of a file after a crash doesn't propagate to the backup */
2015-11-14 17:03:26 +01:00
/* this check is specific for Linux, so we disable it on Windows */
2014-10-04 10:27:23 +02:00
if ( is_original_file_size_different_than_zero & & st - > st_size = = 0 ) {
if ( ! state - > opt . force_zero ) {
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " The file '%s%s' has unexpected zero size! \n " , disk - > dir , sub ) ;
log_fatal ( ESOFT , " It's possible that after a kernel crash this file was lost, \n " ) ;
log_fatal ( ESOFT , " and you can use 'snapraid fix -f /%s' to recover it. \n " , fmt_poll ( disk , sub , esc_buffer ) ) ;
2014-10-25 17:47:22 +02:00
if ( ! is_diff ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " If this an expected condition you can '%s' anyway using 'snapraid --force-zero %s' \n " , state - > command , state - > command ) ;
2014-10-25 17:47:22 +02:00
exit ( EXIT_FAILURE ) ;
}
2014-10-04 10:27:23 +02:00
/* LCOV_EXCL_STOP */
}
}
2015-11-18 23:17:10 +01:00
# else
/* avoid the unused warning in Windows */
( void ) is_original_file_size_different_than_zero ;
2015-11-14 17:03:26 +01:00
# endif
2014-10-04 10:27:23 +02:00
2011-06-20 20:35:04 +02:00
/* insert it */
2013-06-25 22:41:10 +02:00
file = file_alloc ( state - > block_size , sub , st - > st_size , st - > st_mtime , STAT_NSEC ( st ) , st - > st_ino , physical ) ;
2011-03-20 22:36:49 +01:00
/* mark it as present */
2011-04-28 23:09:19 +02:00
file_flag_set ( file , FILE_IS_PRESENT ) ;
2011-03-04 20:59:21 +01:00
2014-05-15 23:45:51 +02:00
/* if copy detection is enabled */
2014-12-09 21:25:17 +01:00
/* note that the copy detection is tried also for updated files */
/* this makes sense because it may happen to have two different copies */
/* of the same file, and we move the right one over the wrong one */
/* in such case we have a "copy" over an "update" */
2014-05-15 23:45:51 +02:00
if ( ! state - > opt . force_nocopy ) {
2014-02-12 20:02:35 +01:00
tommy_uint32_t hash = file_stamp_hash ( file - > size , file - > mtime_sec , file - > mtime_nsec ) ;
2014-05-15 23:45:51 +02:00
/* search for a file with the same name and stamp in all the disks */
2014-09-27 00:32:39 +02:00
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
2014-05-15 23:45:51 +02:00
struct snapraid_disk * other_disk = i - > data ;
struct snapraid_file * other_file ;
2021-11-28 15:50:38 +01:00
stamp_lock ( other_disk ) ;
2014-05-15 23:45:51 +02:00
/* if the nanosecond part of the time stamp is valid, search */
/* for name and stamp, otherwise for path and stamp */
if ( file - > mtime_nsec ! = 0 & & file - > mtime_nsec ! = STAT_NSEC_INVALID )
other_file = tommy_hashdyn_search ( & other_disk - > stampset , file_namestamp_compare , file , hash ) ;
else
other_file = tommy_hashdyn_search ( & other_disk - > stampset , file_pathstamp_compare , file , hash ) ;
2021-11-28 15:50:38 +01:00
stamp_unlock ( other_disk ) ;
2014-05-15 23:45:51 +02:00
/* if found, and it's a fully hashed file */
2015-10-01 18:19:48 +02:00
if ( other_file & & file_is_full_hashed_and_stable ( scan - > state , other_disk , other_file ) ) {
2026-03-14 17:48:30 +01:00
char path_other [ PATH_MAX ] ;
struct stat other_st ;
/*
* Protect the write as multiple threads may write the same FILE_IS_RELOCATED bit .
*
* The bit is always written as 1 and never read , so protection is likely unnecessary
* but still valuable to avoid data race reports from checker tools
*/
stamp_lock ( other_disk ) ;
file_flag_set ( other_file , FILE_IS_RELOCATED ) ;
stamp_unlock ( other_disk ) ;
2014-05-15 23:45:51 +02:00
/* assume that the file is a copy, and reuse the hash */
file_copy ( other_file , file ) ;
2026-03-14 17:48:30 +01:00
/* check if other file still exists */
pathprint ( path_other , sizeof ( path_other ) , " %s%s " , other_disk - > dir , other_file - > sub ) ;
if ( lstat ( path_other , & other_st ) = = 0 ) {
+ + scan - > count_copy ;
2014-05-15 23:45:51 +02:00
2026-03-14 17:48:30 +01:00
log_tag ( " scan:copy:%s:%s:%s:%s \n " , other_disk - > name , esc_tag ( other_file - > sub , esc_buffer ) , disk - > name , esc_tag ( file - > sub , esc_buffer_alt ) ) ;
if ( is_diff ) {
msg_info ( " copy %s -> %s \n " , fmt_term ( other_disk , other_file - > sub , esc_buffer ) , fmt_term ( disk , file - > sub , esc_buffer_alt ) ) ;
}
} else {
+ + scan - > count_relocate ;
log_tag ( " scan:relocate:%s:%s:%s:%s \n " , other_disk - > name , esc_tag ( other_file - > sub , esc_buffer ) , disk - > name , esc_tag ( file - > sub , esc_buffer_alt ) ) ;
if ( is_diff ) {
msg_info ( " relocate %s -> %s \n " , fmt_term ( other_disk , other_file - > sub , esc_buffer ) , fmt_term ( disk , file - > sub , esc_buffer_alt ) ) ;
}
2014-05-15 23:45:51 +02:00
}
2014-03-02 21:56:59 +01:00
2014-12-09 21:25:17 +01:00
/* mark it as reported */
is_file_reported = 1 ;
2014-05-15 23:45:51 +02:00
/* no need to continue the search */
break ;
2014-02-12 20:02:35 +01:00
}
}
}
2014-12-09 21:25:17 +01:00
/* if not yet reported, do it now */
2016-01-18 19:05:46 +01:00
/* we postpone this to avoid to print two times the copied files */
2014-12-09 21:25:17 +01:00
if ( ! is_file_reported ) {
if ( is_file_already_present ) {
+ + scan - > count_change ;
2016-05-29 18:19:13 +02:00
log_tag ( " scan:update:%s:%s: % " PRIu64 " % " PRIu64 " .%d -> % " PRIu64 " % " PRIu64 " .%d \n " , disk - > name , esc_tag ( sub , esc_buffer ) ,
2017-10-27 22:54:42 +02:00
file_already_present_size , file_already_present_mtime_sec , file_already_present_mtime_nsec ,
file - > size , file - > mtime_sec , file - > mtime_nsec
2016-01-18 19:05:46 +01:00
) ;
2014-12-09 21:25:17 +01:00
if ( is_diff ) {
2021-12-01 18:00:07 +01:00
msg_info ( " update %s \n " , fmt_term ( disk , sub , esc_buffer ) ) ;
2014-12-09 21:25:17 +01:00
}
} else {
+ + scan - > count_insert ;
2016-05-29 18:19:13 +02:00
log_tag ( " scan:add:%s:%s \n " , disk - > name , esc_tag ( sub , esc_buffer ) ) ;
2014-12-09 21:25:17 +01:00
if ( is_diff ) {
2021-12-01 18:00:07 +01:00
msg_info ( " add %s \n " , fmt_term ( disk , sub , esc_buffer ) ) ;
2014-12-09 21:25:17 +01:00
}
}
}
2014-03-02 21:56:59 +01:00
2015-10-01 18:19:48 +02:00
/* insert the file in the delayed list */
scan_file_insert ( scan , file ) ;
2011-08-26 14:18:38 +02:00
}
2013-01-27 16:44:04 +01:00
/**
2015-10-07 22:07:20 +02:00
* Remove the specified dir from the data set .
2013-01-27 16:44:04 +01:00
*/
2014-02-12 20:02:35 +01:00
static void scan_emptydir_remove ( struct snapraid_scan * scan , struct snapraid_dir * dir )
2013-01-27 16:44:04 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_disk * disk = scan - > disk ;
2013-01-27 16:44:04 +01:00
/* state changed */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2013-01-27 16:44:04 +01:00
/* remove the file from the dir containers */
tommy_hashdyn_remove_existing ( & disk - > dirset , & dir - > nodeset ) ;
tommy_list_remove_existing ( & disk - > dirlist , & dir - > nodelist ) ;
/* deallocate */
dir_free ( dir ) ;
}
/**
2015-10-07 22:07:20 +02:00
* Insert the specified dir in the data set .
2013-01-27 16:44:04 +01:00
*/
2014-02-12 20:02:35 +01:00
static void scan_emptydir_insert ( struct snapraid_scan * scan , struct snapraid_dir * dir )
2013-01-27 16:44:04 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_disk * disk = scan - > disk ;
2013-01-27 16:44:04 +01:00
/* state changed */
2021-11-28 15:50:38 +01:00
scan - > need_write = 1 ;
2013-01-27 16:44:04 +01:00
/* insert the dir in the dir containers */
tommy_hashdyn_insert ( & disk - > dirset , & dir - > nodeset , dir , dir_name_hash ( dir - > sub ) ) ;
tommy_list_insert_tail ( & disk - > dirlist , & dir - > nodelist , dir ) ;
}
/**
2015-10-07 22:07:20 +02:00
* Process a dir .
2013-01-27 16:44:04 +01:00
*/
2014-02-12 20:02:35 +01:00
static void scan_emptydir ( struct snapraid_scan * scan , const char * sub )
2013-01-27 16:44:04 +01:00
{
2014-02-12 20:02:35 +01:00
struct snapraid_disk * disk = scan - > disk ;
2013-01-27 16:44:04 +01:00
struct snapraid_dir * dir ;
/* check if the dir already exists */
dir = tommy_hashdyn_search ( & disk - > dirset , dir_name_compare , sub , dir_name_hash ( sub ) ) ;
if ( dir ) {
/* check if multiple files have the same name */
if ( dir_flag_has ( dir , FILE_IS_PRESENT ) ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Internal inconsistency for dir '%s%s' \n " , disk - > dir , sub ) ;
2015-12-02 23:21:33 +01:00
os_abort ( ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-01-27 16:44:04 +01:00
}
/* mark as present */
dir_flag_set ( dir , FILE_IS_PRESENT ) ;
/* nothing more to do */
return ;
} else {
/* and continue to insert it */
}
/* insert it */
dir = dir_alloc ( sub ) ;
/* mark it as present */
dir_flag_set ( dir , FILE_IS_PRESENT ) ;
/* insert it in the delayed insert list */
tommy_list_insert_tail ( & scan - > dir_insert_list , & dir - > nodelist , dir ) ;
}
2013-10-02 23:05:22 +02:00
struct dirent_sorted {
/* node for data structures */
tommy_node node ;
# if HAVE_STRUCT_DIRENT_D_INO
uint64_t d_ino ; /**< Inode number. */
# endif
# if HAVE_STRUCT_DIRENT_D_TYPE
uint32_t d_type ; /**< File type. */
# endif
# if HAVE_STRUCT_DIRENT_D_STAT
struct stat d_stat ; /**< Stat result. */
# endif
2014-03-10 21:59:25 +01:00
char d_name [ ] ; /**< Variable length name. It must be the last field. */
2013-10-02 23:05:22 +02:00
} ;
2013-10-03 21:45:48 +02:00
# if HAVE_STRUCT_DIRENT_D_INO
2013-10-02 23:05:22 +02:00
static int dd_ino_compare ( const void * void_a , const void * void_b )
{
const struct dirent_sorted * a = void_a ;
const struct dirent_sorted * b = void_b ;
if ( a - > d_ino < b - > d_ino )
return - 1 ;
if ( a - > d_ino > b - > d_ino )
return 1 ;
return 0 ;
}
# endif
2014-07-11 19:59:01 +02:00
static int dd_name_compare ( const void * void_a , const void * void_b )
{
const struct dirent_sorted * a = void_a ;
const struct dirent_sorted * b = void_b ;
return strcmp ( a - > d_name , b - > d_name ) ;
}
2013-10-02 23:05:22 +02:00
/**
2015-10-07 22:07:20 +02:00
* Return the stat info of a dir entry .
2013-10-02 23:05:22 +02:00
*/
# if HAVE_STRUCT_DIRENT_D_STAT
# define DSTAT(file, dd, buf) dstat(dd)
struct stat * dstat ( struct dirent_sorted * dd )
{
return & dd - > d_stat ;
}
# else
2026-05-03 11:25:24 +02:00
# define DSTAT(file, dd, buf) dstat(disk, file, buf)
struct stat * dstat ( struct snapraid_disk * disk , const char * file , struct stat * st )
2013-10-02 23:05:22 +02:00
{
2026-05-03 11:25:24 +02:00
char esc_buffer [ ESC_MAX ] ;
2013-10-02 23:05:22 +02:00
if ( lstat ( file , st ) ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Stat error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( file , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error in stat file/directory '%s'. %s. \n " , file , strerror ( errno ) ) ;
2013-10-02 23:05:22 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-10-02 23:05:22 +02:00
}
return st ;
}
# endif
2011-03-16 23:52:10 +01:00
/**
2015-10-07 22:07:20 +02:00
* Process a directory .
2013-01-27 16:44:04 +01:00
* Return ! = 0 if at least one file or link is processed .
2011-03-16 23:52:10 +01:00
*/
2022-01-09 10:23:09 +01:00
static int scan_sub ( struct snapraid_scan * scan , int level , int is_diff , char * path_next , char * sub_next , char * tmp )
2011-03-04 20:59:21 +01:00
{
2026-05-03 11:25:24 +02:00
char esc_buffer [ ESC_MAX ] ;
2014-02-12 20:02:35 +01:00
struct snapraid_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
2013-01-27 16:44:04 +01:00
int processed = 0 ;
2011-03-04 20:59:21 +01:00
DIR * d ;
2013-10-02 23:05:22 +02:00
tommy_list list ;
tommy_node * node ;
2022-01-09 10:23:09 +01:00
size_t path_len ;
size_t sub_len ;
path_len = strlen ( path_next ) ;
sub_len = strlen ( sub_next ) ;
2013-10-02 23:05:22 +02:00
tommy_list_init ( & list ) ;
2011-03-04 20:59:21 +01:00
2022-01-09 10:23:09 +01:00
d = opendir ( path_next ) ;
2011-03-04 20:59:21 +01:00
if ( ! d ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Open dir error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error opening directory '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2015-06-23 21:39:18 +02:00
if ( level = = 0 )
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " If this is the disk mount point, remember to create it manually \n " ) ;
2015-06-23 21:39:18 +02:00
else
2026-05-03 11:25:24 +02:00
log_fatal ( errno , " If it's a permission problem, you can exclude it in the config file with: exclude /%s \n " , sub_next ) ;
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
}
2013-10-02 23:05:22 +02:00
/* read the full directory */
while ( 1 ) {
struct dirent_sorted * entry ;
2011-05-04 19:28:36 +02:00
const char * name ;
struct dirent * dd ;
2013-10-02 23:05:22 +02:00
size_t name_len ;
2011-05-04 19:28:36 +02:00
2016-09-23 16:46:54 +02:00
/*
* Clear errno to differentiate the end of the stream and an error condition
*
* From the Linux readdir ( ) manpage :
* " If the end of the directory stream is reached, NULL is returned and errno is not changed.
* If an error occurs , NULL is returned and errno is set appropriately . "
*/
2011-05-04 19:28:36 +02:00
errno = 0 ;
dd = readdir ( d ) ;
if ( dd = = 0 & & errno ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2022-01-09 10:23:09 +01:00
/* restore removing additions */
path_next [ path_len ] = 0 ;
sub_next [ sub_len ] = 0 ;
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Read dir error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error reading directory '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2026-05-03 11:25:24 +02:00
log_fatal ( errno , " You can exclude it in the config file with: exclude /%s \n " , sub_next ) ;
2011-05-04 19:28:36 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-05-04 19:28:36 +02:00
}
2013-02-27 21:07:16 +01:00
if ( dd = = 0 ) {
2011-05-04 19:28:36 +02:00
break ; /* finished */
}
2011-03-20 22:36:49 +01:00
/* skip "." and ".." files */
2011-05-04 19:28:36 +02:00
name = dd - > d_name ;
2011-03-22 23:23:45 +01:00
if ( name [ 0 ] = = ' . ' & & ( name [ 1 ] = = 0 | | ( name [ 1 ] = = ' . ' & & name [ 2 ] = = 0 ) ) )
2011-03-20 22:36:49 +01:00
continue ;
2011-03-12 10:09:59 +01:00
2022-01-09 10:23:09 +01:00
pathcatl ( path_next , path_len , PATH_MAX , name ) ;
2011-06-26 21:37:47 +02:00
2013-10-01 18:22:41 +02:00
/* check for not supported file names */
if ( name [ 0 ] = = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Unsupported name error. \n " , es ( ESOFT ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " Unsupported name '%s' in file '%s'. \n " , name , path_next ) ;
2011-05-04 19:28:36 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-05-04 19:28:36 +02:00
}
2013-06-25 22:41:10 +02:00
/* exclude hidden files even before calling lstat() */
2013-01-18 00:15:55 +01:00
if ( filter_hidden ( state - > filter_hidden , dd ) ! = 0 ) {
2015-02-22 14:31:03 +01:00
msg_verbose ( " Excluding hidden '%s' \n " , path_next ) ;
2012-11-04 00:46:16 +01:00
continue ;
}
2013-05-14 21:34:51 +02:00
/* exclude content files even before calling lstat() */
2013-01-18 00:15:55 +01:00
if ( filter_content ( & state - > contentlist , path_next ) ! = 0 ) {
2015-02-22 14:31:03 +01:00
msg_verbose ( " Excluding content '%s' \n " , path_next ) ;
2013-01-18 00:15:55 +01:00
continue ;
}
2013-10-02 23:05:22 +02:00
name_len = strlen ( dd - > d_name ) ;
2014-03-10 21:59:25 +01:00
entry = malloc_nofail ( sizeof ( struct dirent_sorted ) + name_len + 1 ) ;
2013-10-02 23:05:22 +02:00
/* copy the dir entry */
# if HAVE_STRUCT_DIRENT_D_INO
entry - > d_ino = dd - > d_ino ;
# endif
# if HAVE_STRUCT_DIRENT_D_TYPE
entry - > d_type = dd - > d_type ;
# endif
# if HAVE_STRUCT_DIRENT_D_STAT
2013-01-18 00:15:55 +01:00
/* convert dirent to lstat result */
2013-10-02 23:05:22 +02:00
dirent_lstat ( dd , & entry - > d_stat ) ;
2014-10-01 21:44:35 +02:00
/* note that at this point the st_mode may be 0 */
2013-10-02 23:05:22 +02:00
# endif
memcpy ( entry - > d_name , dd - > d_name , name_len + 1 ) ;
/* insert in the list */
tommy_list_insert_tail ( & list , & entry - > node , entry ) ;
2026-01-10 16:30:35 +01:00
/* process ignore files */
if ( strcmp ( " .snapraidignore " , dd - > d_name ) = = 0 )
state_load_ignore_file ( & scan - > local_filter_list , path_next , sub_next ) ;
2013-10-02 23:05:22 +02:00
}
if ( closedir ( d ) ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2022-01-09 10:23:09 +01:00
/* restore removing additions */
path_next [ path_len ] = 0 ;
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Close dir error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error closing directory '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2013-10-02 23:05:22 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-10-02 23:05:22 +02:00
}
2014-07-11 19:59:01 +02:00
if ( state - > opt . force_order = = SORT_ALPHA ) {
/* if requested sort alphabetically */
/* this is mainly done for testing to ensure to always */
/* process in the same way in different platforms */
tommy_list_sort ( & list , dd_name_compare ) ;
2014-07-11 23:33:19 +02:00
}
# if HAVE_STRUCT_DIRENT_D_INO
else if ( ! disk - > has_volatile_inodes ) {
2014-07-11 19:59:01 +02:00
/* if inodes are persistent */
2013-10-02 23:05:22 +02:00
/* sort the list of dir entries by inodes */
tommy_list_sort ( & list , dd_ino_compare ) ;
}
2014-07-11 19:59:01 +02:00
/* otherwise just keep the insertion order */
2013-10-02 23:05:22 +02:00
# endif
/* process the sorted dir entries */
node = list ;
2014-09-27 00:32:39 +02:00
while ( node ! = 0 ) {
2015-01-27 23:10:38 +01:00
struct snapraid_filter * reason = 0 ;
2013-10-02 23:05:22 +02:00
struct dirent_sorted * dd = node - > data ;
const char * name = dd - > d_name ;
struct stat * st ;
int type ;
# if !HAVE_STRUCT_DIRENT_D_STAT
struct stat st_buf ;
# endif
2022-01-09 10:23:09 +01:00
pathcatl ( path_next , path_len , PATH_MAX , name ) ;
pathcatl ( sub_next , sub_len , PATH_MAX , name ) ;
2013-10-02 23:05:22 +02:00
/* start with an unknown type */
type = - 1 ;
st = 0 ;
/* if dirent has the type, use it */
# if HAVE_STRUCT_DIRENT_D_TYPE
switch ( dd - > d_type ) {
case DT_UNKNOWN : break ;
case DT_REG : type = 0 ; break ;
case DT_LNK : type = 1 ; break ;
case DT_DIR : type = 2 ; break ;
default : type = 3 ; break ;
2011-03-22 23:23:45 +01:00
}
2013-01-18 00:15:55 +01:00
# endif
2011-03-22 23:23:45 +01:00
2013-10-02 23:05:22 +02:00
/* if type is still unknown */
if ( type < 0 ) {
/* get the type from stat */
st = DSTAT ( path_next , dd , & st_buf ) ;
2014-10-01 21:44:35 +02:00
# if HAVE_STRUCT_DIRENT_D_STAT
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
2014-10-14 23:01:01 +02:00
/* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
2014-10-19 21:09:22 +02:00
/* because we use a directory reading method that doesn't read info about ReparsePoint. */
2014-10-14 23:01:01 +02:00
/* Note that here we cannot call here lstat_sync(), because we don't know what kind */
/* of file is it, and lstat_sync() doesn't always work */
2014-11-12 20:47:07 +01:00
if ( st - > st_mode = = 0 ) {
2014-10-01 21:44:35 +02:00
if ( lstat ( path_next , st ) ! = 0 ) {
2014-09-30 21:54:49 +02:00
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Stat error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error in stat file/directory '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2014-09-30 21:54:49 +02:00
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
}
# endif
2013-10-02 23:05:22 +02:00
if ( S_ISREG ( st - > st_mode ) )
type = 0 ;
else if ( S_ISLNK ( st - > st_mode ) )
type = 1 ;
else if ( S_ISDIR ( st - > st_mode ) )
type = 2 ;
else
type = 3 ;
}
if ( type = = 0 ) { /* REG */
2026-01-10 16:30:35 +01:00
if ( filter_path ( & state - > filterlist , & reason , disk - > name , sub_next ) = = 0
& & filter_path ( & scan - > local_filter_list , & reason , disk - > name , sub_next ) = = 0 ) {
2013-06-25 22:41:10 +02:00
2014-09-30 21:54:49 +02:00
/* late stat, if not yet called */
if ( ! st )
st = DSTAT ( path_next , dd , & st_buf ) ;
2013-10-02 23:05:22 +02:00
2014-10-04 10:27:23 +02:00
# if HAVE_LSTAT_SYNC
2014-10-01 21:44:35 +02:00
/* if the st_ino field is missing, takes care to fill it using the extended lstat() */
/* this can happen only in Windows */
2017-10-29 18:07:01 +01:00
if ( st - > st_ino = = 0 | | st - > st_nlink = = 0 ) {
2014-10-14 22:47:21 +02:00
if ( lstat_sync ( path_next , st , 0 ) ! = 0 ) {
2014-09-30 21:54:49 +02:00
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Stat error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error in stat file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2014-09-30 21:54:49 +02:00
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
2011-05-04 19:28:36 +02:00
}
# endif
2014-09-30 21:54:49 +02:00
2014-10-25 17:47:22 +02:00
scan_file ( scan , is_diff , sub_next , st , FILEPHY_UNREAD_OFFSET ) ;
2013-01-27 16:44:04 +01:00
processed = 1 ;
2011-03-16 23:52:10 +01:00
} else {
2022-01-09 10:23:09 +01:00
msg_verbose ( " Excluding file '%s' for rule '%s' \n " , path_next , filter_type ( reason , tmp , PATH_MAX ) ) ;
2011-03-16 23:52:10 +01:00
}
2013-10-02 23:05:22 +02:00
} else if ( type = = 1 ) { /* LNK */
2026-01-10 16:30:35 +01:00
if ( filter_path ( & state - > filterlist , & reason , disk - > name , sub_next ) = = 0
& & filter_path ( & scan - > local_filter_list , & reason , disk - > name , sub_next ) = = 0 ) {
2011-08-26 14:18:38 +02:00
int ret ;
2022-01-09 10:23:09 +01:00
ret = readlink ( path_next , tmp , PATH_MAX ) ;
2011-08-26 14:18:38 +02:00
if ( ret > = PATH_MAX ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Readlink error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( EINTERNAL , " Error in readlink file '%s'. Symlink too long. \n " , path_next ) ;
2011-08-26 14:18:38 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-08-26 14:18:38 +02:00
}
if ( ret < 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Readlink error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error in readlink file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2011-08-26 14:18:38 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-08-26 14:18:38 +02:00
}
2015-12-18 19:41:49 +01:00
if ( ret = = 0 )
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Empty symbolic link '%s'. \n " , path_next ) ;
2011-08-26 14:18:38 +02:00
/* readlink doesn't put the final 0 */
2022-01-09 08:46:06 +01:00
tmp [ ret ] = 0 ;
2011-08-26 14:18:38 +02:00
2013-01-27 18:06:15 +01:00
/* process as a symbolic link */
2022-01-09 08:46:06 +01:00
scan_link ( scan , is_diff , sub_next , tmp , FILE_IS_SYMLINK ) ;
2013-01-27 16:44:04 +01:00
processed = 1 ;
2011-08-26 14:18:38 +02:00
} else {
2022-01-09 10:23:09 +01:00
msg_verbose ( " Excluding link '%s' for rule '%s' \n " , path_next , filter_type ( reason , tmp , PATH_MAX ) ) ;
2011-08-26 14:18:38 +02:00
}
2013-10-02 23:05:22 +02:00
} else if ( type = = 2 ) { /* DIR */
2026-01-10 16:30:35 +01:00
if ( filter_subdir ( & state - > filterlist , & reason , disk - > name , sub_next ) = = 0
& & filter_subdir ( & scan - > local_filter_list , & reason , disk - > name , sub_next ) = = 0 ) {
2013-07-24 21:56:52 +02:00
# ifndef _WIN32
2014-10-01 21:44:35 +02:00
/* late stat, if not yet called */
if ( ! st )
st = DSTAT ( path_next , dd , & st_buf ) ;
2013-10-02 23:05:22 +02:00
2026-05-03 11:25:24 +02:00
/*
* In Unix don ' t follow mount points in different devices
* in Windows we are already skipping them reporting them as special files
*/
2013-10-02 23:05:22 +02:00
if ( ( uint64_t ) st - > st_dev ! = disk - > device ) {
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Ignoring mount point. \n " , es ( ESOFT ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Ignoring mount point '%s' because it appears to be in a different device \n " , path_next ) ;
2013-07-24 21:56:52 +02:00
} else
# endif
{
2013-06-21 23:57:58 +02:00
/* recurse */
2022-01-09 10:23:09 +01:00
pathslash ( path_next , PATH_MAX ) ;
pathslash ( sub_next , PATH_MAX ) ;
if ( scan_sub ( scan , level + 1 , is_diff , path_next , sub_next , tmp ) = = 0 ) {
/* restore removing additions */
pathcatl ( sub_next , sub_len , PATH_MAX , name ) ;
2013-06-21 23:57:58 +02:00
/* scan the directory as empty dir */
2014-02-12 20:02:35 +01:00
scan_emptydir ( scan , sub_next ) ;
2013-06-21 23:57:58 +02:00
}
/* or we processed something internally, or we have added the empty dir */
processed = 1 ;
2013-01-27 16:44:04 +01:00
}
2011-03-20 22:36:49 +01:00
} else {
2022-01-09 10:23:09 +01:00
msg_verbose ( " Excluding directory '%s' for rule '%s' \n " , path_next , filter_type ( reason , tmp , PATH_MAX ) ) ;
2011-03-04 20:59:21 +01:00
}
} else {
2026-01-10 16:30:35 +01:00
if ( filter_path ( & state - > filterlist , & reason , disk - > name , sub_next ) = = 0
& & filter_path ( & scan - > local_filter_list , & reason , disk - > name , sub_next ) = = 0 ) {
2014-10-01 21:44:35 +02:00
/* late stat, if not yet called */
if ( ! st )
st = DSTAT ( path_next , dd , & st_buf ) ;
2014-09-27 00:32:39 +02:00
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Ignoring special file. \n " , es ( ESOFT ) , 0 , disk - > name , esc_tag ( path_next , esc_buffer ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Ignoring special '%s' file '%s' \n " , stat_desc ( st ) , path_next ) ;
2011-06-11 09:38:19 +02:00
} else {
2022-01-09 10:23:09 +01:00
msg_verbose ( " Excluding special file '%s' for rule '%s' \n " , path_next , filter_type ( reason , tmp , PATH_MAX ) ) ;
2011-03-04 20:59:21 +01:00
}
}
2013-10-02 23:05:22 +02:00
/* next entry */
node = node - > next ;
/* free the present one */
free ( dd ) ;
2011-05-04 19:28:36 +02:00
}
2013-01-27 16:44:04 +01:00
return processed ;
2011-03-04 20:59:21 +01:00
}
2022-01-09 10:23:09 +01:00
/**
* Process a directory .
* Return ! = 0 if at least one file or link is processed .
*/
static int scan_dir ( struct snapraid_scan * scan , int level , int is_diff , const char * dir , const char * sub )
{
/* working buffers used by scan_sub() */
char path_next [ PATH_MAX ] ;
char sub_next [ PATH_MAX ] ;
char tmp [ PATH_MAX ] ;
pathcpy ( path_next , sizeof ( path_next ) , dir ) ;
pathcpy ( sub_next , sizeof ( sub_next ) , sub ) ;
return scan_sub ( scan , level , is_diff , path_next , sub_next , tmp ) ;
}
2021-11-28 15:50:38 +01:00
static void * scan_disk ( void * arg )
{
2026-05-03 11:25:24 +02:00
char esc_buffer [ ESC_MAX ] ;
2021-11-28 15:50:38 +01:00
struct snapraid_scan * scan = arg ;
struct snapraid_disk * disk = scan - > disk ;
int ret ;
int has_persistent_inodes ;
int has_syncronized_hardlinks ;
2021-11-29 16:50:30 +01:00
uint64_t start ;
2021-11-28 15:50:38 +01:00
/* check if the disk supports persistent inodes */
2026-01-29 18:43:00 +01:00
ret = fsinfo ( disk - > dir , & has_persistent_inodes , & has_syncronized_hardlinks , 0 , 0 , 0 , 0 , 0 , 0 ) ;
2021-11-28 15:50:38 +01:00
if ( ret < 0 ) {
/* LCOV_EXCL_START */
2026-05-03 11:25:24 +02:00
log_tag ( " %s:%u:%s:%s: Filesystem info error. %s. \n " , es ( errno ) , 0 , disk - > name , esc_tag ( disk - > dir , esc_buffer ) , strerror ( errno ) ) ;
2026-02-01 20:21:28 +01:00
log_fatal ( errno , " Error accessing disk '%s' to get file-system info. %s. \n " , disk - > dir , strerror ( errno ) ) ;
2021-11-28 15:50:38 +01:00
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
if ( ! has_persistent_inodes ) {
disk - > has_volatile_inodes = 1 ;
}
if ( ! has_syncronized_hardlinks ) {
disk - > has_volatile_hardlinks = 1 ;
}
/* if inodes or UUID are not persistent/changed/unsupported */
if ( disk - > has_volatile_inodes | | disk - > has_different_uuid | | disk - > has_unsupported_uuid ) {
/* remove all the inodes from the inode collection */
/* if they are not persistent, all of them could be changed now */
/* and we don't want to find false matching ones */
/* see scan_file() for more details */
tommy_node * node = disk - > filelist ;
while ( node ) {
struct snapraid_file * file = node - > data ;
node = node - > next ;
/* remove from the inode set */
tommy_hashdyn_remove_existing ( & disk - > inodeset , & file - > nodeset ) ;
/* clear the inode */
file - > inode = 0 ;
/* mark as missing inode */
file_flag_set ( file , FILE_IS_WITHOUT_INODE ) ;
}
}
2026-03-02 01:13:19 +01:00
start = os_tick_ms ( ) ;
2021-11-29 16:50:30 +01:00
2021-11-28 15:50:38 +01:00
scan_dir ( scan , 0 , scan - > is_diff , disk - > dir , " " ) ;
2021-11-29 16:50:30 +01:00
if ( ! scan - > is_diff )
2026-03-02 01:13:19 +01:00
msg_progress ( " Scanned %s in % " PRIu64 " seconds \n " , disk - > name , ( os_tick_ms ( ) - start ) / 1000 ) ;
2021-11-29 16:50:30 +01:00
2021-11-28 15:50:38 +01:00
return 0 ;
}
2015-03-02 23:01:15 +01:00
static int state_diffscan ( struct snapraid_state * state , int is_diff )
2011-03-04 20:59:21 +01:00
{
2011-09-23 21:48:50 +02:00
tommy_node * i ;
tommy_node * j ;
tommy_list scanlist ;
2014-04-03 23:02:43 +02:00
int done ;
2026-02-01 20:21:28 +01:00
msg_ptr * msg ;
2015-02-22 14:31:03 +01:00
struct snapraid_scan total ;
int no_difference ;
2016-02-09 23:12:33 +01:00
char esc_buffer [ ESC_MAX ] ;
2011-09-23 21:48:50 +02:00
tommy_list_init ( & scanlist ) ;
2011-03-04 20:59:21 +01:00
2014-10-25 17:47:22 +02:00
if ( is_diff )
2015-02-22 14:31:03 +01:00
msg_progress ( " Comparing... \n " ) ;
2021-11-28 15:50:38 +01:00
else
msg_progress ( " Scanning... \n " ) ;
2013-10-19 13:24:35 +02:00
2026-02-09 15:50:58 +01:00
log_tag ( " list:scan_begin \n " ) ;
2022-01-09 08:46:06 +01:00
/* allocate all the scan data */
2014-09-27 00:32:39 +02:00
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
2011-09-23 21:48:50 +02:00
struct snapraid_disk * disk = i - > data ;
struct snapraid_scan * scan ;
2013-09-25 23:20:07 +02:00
2021-11-28 15:50:38 +01:00
scan = scan_alloc ( state , disk , is_diff ) ;
2013-09-25 23:20:07 +02:00
2021-11-28 15:50:38 +01:00
tommy_list_insert_tail ( & scanlist , & scan - > node , scan ) ;
}
2013-09-25 23:20:07 +02:00
2021-11-28 15:50:38 +01:00
/* first scan all the directory and find new and deleted files */
for ( i = scanlist ; i ! = 0 ; i = i - > next ) {
struct snapraid_scan * scan = i - > data ;
# if HAVE_THREAD
2022-01-07 21:03:51 +01:00
if ( state - > opt . skip_multi_scan )
scan_disk ( scan ) ;
else
thread_create ( & scan - > thread , scan_disk , scan ) ;
2021-11-28 15:50:38 +01:00
# else
scan_disk ( scan ) ;
# endif
}
2013-09-25 23:20:07 +02:00
2021-11-28 15:50:38 +01:00
# if HAVE_THREAD
/* wait for all threads to terminate */
for ( i = scanlist ; i ! = 0 ; i = i - > next ) {
struct snapraid_scan * scan = i - > data ;
void * retval ;
2013-08-27 23:02:12 +02:00
2021-11-28 15:50:38 +01:00
/* wait for thread termination */
2022-01-07 21:03:51 +01:00
if ( ! state - > opt . skip_multi_scan )
thread_join ( scan - > thread , & retval ) ;
2014-02-12 20:02:35 +01:00
}
2021-11-28 15:50:38 +01:00
# endif
2014-02-12 20:02:35 +01:00
/* we split the search in two phases because to detect files */
/* moved from one disk to another we have to start deletion */
/* only when all disks have all the new files found */
/* now process all the new and deleted files */
2014-09-27 00:32:39 +02:00
for ( i = scanlist ; i ! = 0 ; i = i - > next ) {
2014-02-12 20:02:35 +01:00
struct snapraid_scan * scan = i - > data ;
struct snapraid_disk * disk = scan - > disk ;
tommy_node * node ;
unsigned phy_dup ;
uint64_t phy_last ;
struct snapraid_file * phy_file_last ;
2011-03-04 20:59:21 +01:00
/* check for removed files */
node = disk - > filelist ;
while ( node ) {
struct snapraid_file * file = node - > data ;
/* next node */
node = node - > next ;
/* remove if not present */
2011-04-28 23:09:19 +02:00
if ( ! file_flag_has ( file , FILE_IS_PRESENT ) ) {
2026-03-14 17:48:30 +01:00
if ( ! file_flag_has ( file , FILE_IS_RELOCATED ) ) {
+ + scan - > count_remove ;
2011-06-20 20:35:04 +02:00
2026-03-14 17:48:30 +01:00
log_tag ( " scan:remove:%s:%s \n " , disk - > name , esc_tag ( file - > sub , esc_buffer ) ) ;
if ( is_diff ) {
msg_info ( " remove %s \n " , fmt_term ( disk , file - > sub , esc_buffer ) ) ;
}
2011-06-20 20:35:04 +02:00
}
2014-02-12 20:02:35 +01:00
scan_file_remove ( scan , file ) ;
2011-03-04 20:59:21 +01:00
}
}
2011-08-26 14:18:38 +02:00
/* check for removed links */
node = disk - > linklist ;
while ( node ) {
2015-10-08 22:19:52 +02:00
struct snapraid_link * slink = node - > data ;
2011-08-26 14:18:38 +02:00
/* next node */
node = node - > next ;
/* remove if not present */
2015-10-08 22:19:52 +02:00
if ( ! link_flag_has ( slink , FILE_IS_PRESENT ) ) {
2011-09-23 21:48:50 +02:00
+ + scan - > count_remove ;
2011-08-26 14:18:38 +02:00
2016-05-29 18:19:13 +02:00
log_tag ( " scan:remove:%s:%s \n " , disk - > name , esc_tag ( slink - > sub , esc_buffer ) ) ;
2014-10-25 17:47:22 +02:00
if ( is_diff ) {
2021-12-01 18:00:07 +01:00
msg_info ( " remove %s \n " , fmt_term ( disk , slink - > sub , esc_buffer ) ) ;
2011-08-26 14:18:38 +02:00
}
2015-10-08 22:19:52 +02:00
scan_link_remove ( scan , slink ) ;
2011-08-26 14:18:38 +02:00
}
}
2013-01-27 16:44:04 +01:00
/* check for removed dirs */
node = disk - > dirlist ;
while ( node ) {
struct snapraid_dir * dir = node - > data ;
/* next node */
node = node - > next ;
/* remove if not present */
if ( ! dir_flag_has ( dir , FILE_IS_PRESENT ) ) {
2014-02-12 20:02:35 +01:00
scan_emptydir_remove ( scan , dir ) ;
2013-01-27 16:44:04 +01:00
}
}
2013-06-25 22:41:10 +02:00
/* sort the files before inserting them */
2013-09-24 21:45:07 +02:00
/* we use a stable sort to ensure that if the reported physical offset/inode */
/* are always 0, we keep at least the directory order */
2013-06-29 18:11:36 +02:00
switch ( state - > opt . force_order ) {
2013-06-25 22:41:10 +02:00
case SORT_PHYSICAL :
tommy_list_sort ( & scan - > file_insert_list , file_physical_compare ) ;
break ;
case SORT_INODE :
tommy_list_sort ( & scan - > file_insert_list , file_inode_compare ) ;
break ;
case SORT_ALPHA :
2014-02-12 20:02:35 +01:00
tommy_list_sort ( & scan - > file_insert_list , file_path_compare ) ;
2013-06-25 22:41:10 +02:00
break ;
case SORT_DIR :
/* already in order */
break ;
}
2011-05-24 21:49:15 +02:00
/* insert all the new files, we insert them only after the deletion */
2011-09-03 22:21:45 +02:00
/* to reuse the just freed space */
2014-10-03 21:28:56 +02:00
/* also check if the physical offset reported are fakes or not */
2011-09-23 21:48:50 +02:00
node = scan - > file_insert_list ;
2013-09-25 23:20:07 +02:00
phy_dup = 0 ;
2014-10-03 21:28:56 +02:00
phy_last = FILEPHY_UNREAD_OFFSET ;
2013-12-02 19:03:00 +01:00
phy_file_last = 0 ;
2011-05-24 21:49:15 +02:00
while ( node ) {
struct snapraid_file * file = node - > data ;
2013-09-25 23:20:07 +02:00
/* if the file is not empty, count duplicate physical offsets */
if ( state - > opt . force_order = = SORT_PHYSICAL & & file - > size ! = 0 ) {
2014-10-03 21:28:56 +02:00
if ( phy_file_last ! = 0 & & file - > physical = = phy_last
2014-11-12 20:47:07 +01:00
/* files without offset are expected to have duplicates */
2014-10-03 21:28:56 +02:00
& & phy_last ! = FILEPHY_WITHOUT_OFFSET
) {
2015-10-07 22:07:20 +02:00
/* if verbose, print the list of duplicates real offsets */
2014-10-03 21:28:56 +02:00
/* other cases are for offsets not supported, so we don't need to report them file by file */
2015-02-22 14:31:03 +01:00
if ( phy_last > = FILEPHY_REAL_OFFSET ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Files '%s%s' and '%s%s' share the same physical offset % " PRId64 " . \n " , disk - > dir , phy_file_last - > sub , disk - > dir , file - > sub , phy_last ) ;
2013-12-02 19:03:00 +01:00
}
2013-09-25 23:20:07 +02:00
+ + phy_dup ;
2013-12-02 19:03:00 +01:00
}
phy_file_last = file ;
2013-09-25 23:20:07 +02:00
phy_last = file - > physical ;
}
2011-05-24 21:49:15 +02:00
/* next node */
node = node - > next ;
2015-10-01 18:19:48 +02:00
/* insert in the parity */
scan_file_allocate ( scan , file ) ;
2011-05-24 21:49:15 +02:00
}
2013-09-25 23:20:07 +02:00
/* mark the disk without reliable physical offset if it has duplicates */
/* here it should never happen because we already sorted out hardlinks */
if ( state - > opt . force_order = = SORT_PHYSICAL & & phy_dup > 0 ) {
2013-12-14 17:54:04 +01:00
disk - > has_unreliable_physical = 1 ;
2013-09-25 23:20:07 +02:00
}
2011-08-26 14:18:38 +02:00
/* insert all the new links */
2011-09-23 21:48:50 +02:00
node = scan - > link_insert_list ;
2011-08-26 14:18:38 +02:00
while ( node ) {
2015-10-08 22:19:52 +02:00
struct snapraid_link * slink = node - > data ;
2011-08-26 14:18:38 +02:00
/* next node */
node = node - > next ;
/* insert it */
2015-10-08 22:19:52 +02:00
scan_link_insert ( scan , slink ) ;
2011-08-26 14:18:38 +02:00
}
2013-01-27 16:44:04 +01:00
/* insert all the new dirs */
node = scan - > dir_insert_list ;
while ( node ) {
struct snapraid_dir * dir = node - > data ;
/* next node */
node = node - > next ;
/* insert it */
2014-02-12 20:02:35 +01:00
scan_emptydir_insert ( scan , dir ) ;
2013-01-27 16:44:04 +01:00
}
2011-09-03 10:12:13 +02:00
}
2021-11-28 15:50:38 +01:00
/* propagate the state change (after all the scan operations are called) */
for ( i = scanlist ; i ! = 0 ; i = i - > next ) {
struct snapraid_scan * scan = i - > data ;
if ( scan - > need_write ) {
state - > need_write = 1 ;
}
}
2015-10-07 22:07:20 +02:00
/* check for disks where all the previously existing files where removed */
2013-06-29 18:11:36 +02:00
if ( ! state - > opt . force_empty ) {
2015-04-30 22:05:53 +02:00
int all_missing = 0 ;
int all_rewritten = 0 ;
2014-04-03 23:02:43 +02:00
done = 0 ;
2014-09-27 00:32:39 +02:00
for ( i = state - > disklist , j = scanlist ; i ! = 0 ; i = i - > next , j = j - > next ) {
2011-09-23 21:48:50 +02:00
struct snapraid_disk * disk = i - > data ;
struct snapraid_scan * scan = j - > data ;
2011-09-03 10:19:18 +02:00
2015-04-30 22:05:53 +02:00
if ( scan - > count_equal = = 0
& & scan - > count_move = = 0
& & scan - > count_restore = = 0
& & ( scan - > count_remove ! = 0 | | scan - > count_change ! = 0 )
) {
2013-09-25 23:20:07 +02:00
if ( ! done ) {
done = 1 ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! All the files previously present in disk '%s' at dir '%s' " , disk - > name , disk - > dir ) ;
2011-09-03 10:19:18 +02:00
} else {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " , disk '%s' at dir '%s' " , disk - > name , disk - > dir ) ;
2011-09-03 10:19:18 +02:00
}
2015-04-30 22:05:53 +02:00
/* detect the special condition of all files missing */
if ( scan - > count_change = = 0 )
all_missing = 1 ;
/* detect the special condition of all files rewritten */
if ( scan - > count_remove = = 0 )
all_rewritten = 1 ;
2011-03-04 20:59:21 +01:00
}
}
2013-09-25 23:20:07 +02:00
if ( done ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " \n are now missing or have been rewritten! \n " ) ;
2015-04-30 22:05:53 +02:00
if ( all_rewritten ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " This could occur when restoring a disk from a backup \n " ) ;
log_fatal ( ESOFT , " program that is not setting correctly the timestamps. \n " ) ;
2015-04-30 22:05:53 +02:00
}
if ( all_missing ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " This could occur when some disks are not mounted \n " ) ;
log_fatal ( ESOFT , " in the expected directory. \n " ) ;
2015-04-30 22:05:53 +02:00
}
2014-10-25 17:47:22 +02:00
if ( ! is_diff ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " If you want to '%s' anyway, use 'snapraid --force-empty %s'. \n " , state - > command , state - > command ) ;
2014-10-25 17:47:22 +02:00
exit ( EXIT_FAILURE ) ;
}
2011-09-03 10:19:18 +02:00
}
2011-09-03 10:12:13 +02:00
}
2011-03-04 20:59:21 +01:00
2015-10-07 22:07:20 +02:00
/* check for disks without the physical offset support */
2013-09-25 23:20:07 +02:00
if ( state - > opt . force_order = = SORT_PHYSICAL ) {
2014-04-03 23:02:43 +02:00
done = 0 ;
2014-09-27 00:32:39 +02:00
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
2013-09-25 23:20:07 +02:00
struct snapraid_disk * disk = i - > data ;
2013-12-14 17:54:04 +01:00
if ( disk - > has_unreliable_physical ) {
2013-09-25 23:20:07 +02:00
if ( ! done ) {
done = 1 ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Physical offsets not supported for disk '%s' " , disk - > name ) ;
2013-09-25 23:20:07 +02:00
} else {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " , '%s' " , disk - > name ) ;
2013-09-25 23:20:07 +02:00
}
}
}
if ( done ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " . The order of files won't be optimal. \n " ) ;
2013-09-25 23:20:07 +02:00
}
}
2015-10-07 22:07:20 +02:00
/* check for disks without persistent inodes */
2014-04-03 23:02:43 +02:00
done = 0 ;
2014-09-27 00:32:39 +02:00
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
2014-04-03 23:02:43 +02:00
struct snapraid_disk * disk = i - > data ;
2013-12-14 17:54:04 +01:00
2014-04-03 23:02:43 +02:00
if ( disk - > has_volatile_inodes ) {
if ( ! done ) {
done = 1 ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! Inodes are not persistent for disks: '%s' " , disk - > name ) ;
2014-04-03 23:02:43 +02:00
} else {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " , '%s' " , disk - > name ) ;
2013-12-14 17:54:04 +01:00
}
}
2014-04-03 23:02:43 +02:00
}
if ( done ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " . Inodes are not used to detect move operations. \n " ) ;
2013-12-14 17:54:04 +01:00
}
2015-10-07 22:07:20 +02:00
/* check for disks with changed UUID */
2014-04-03 23:02:43 +02:00
done = 0 ;
2014-09-27 00:32:39 +02:00
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
2014-04-03 23:02:43 +02:00
struct snapraid_disk * disk = i - > data ;
2013-09-25 23:20:07 +02:00
2014-04-03 23:02:43 +02:00
/* don't print the message if the UUID changed because before */
/* it was no set. */
/* this is the normal condition for an empty disk because it */
/* isn't stored */
if ( disk - > has_different_uuid & & ! disk - > had_empty_uuid ) {
if ( ! done ) {
done = 1 ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! UUID is changed for disks: '%s' " , disk - > name ) ;
2014-04-03 23:02:43 +02:00
} else {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " , '%s' " , disk - > name ) ;
2013-09-25 23:20:07 +02:00
}
}
2014-04-03 23:02:43 +02:00
}
if ( done ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " . Inodes are not used to detect move operations. \n " ) ;
2014-04-03 23:02:43 +02:00
}
2015-10-07 22:07:20 +02:00
/* check for disks with unsupported UUID */
2014-04-03 23:02:43 +02:00
done = 0 ;
2014-09-27 00:32:39 +02:00
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
2014-04-03 23:02:43 +02:00
struct snapraid_disk * disk = i - > data ;
if ( disk - > has_unsupported_uuid ) {
if ( ! done ) {
done = 1 ;
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " WARNING! UUID is unsupported for disks: '%s' " , disk - > name ) ;
2014-04-03 23:02:43 +02:00
} else {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " , '%s' " , disk - > name ) ;
2014-04-03 23:02:43 +02:00
}
2013-09-25 23:20:07 +02:00
}
}
2014-04-03 23:02:43 +02:00
if ( done ) {
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " . Not using inodes to detect move operations. \n " ) ;
2016-01-05 23:35:55 +01:00
# if defined(_linux) && !HAVE_BLKID
2026-02-01 20:21:28 +01:00
log_fatal ( ESOFT , " The 'blkid' library is not linked in SnapRAID! \n " ) ;
log_fatal ( ESOFT , " Try rebuilding it after installing the libblkid-dev or libblkid-devel package. \n " ) ;
2016-01-05 23:35:55 +01:00
# endif
2014-04-03 23:02:43 +02:00
}
2015-02-22 14:31:03 +01:00
total . count_equal = 0 ;
total . count_move = 0 ;
total . count_copy = 0 ;
2026-03-14 17:48:30 +01:00
total . count_relocate = 0 ;
2015-02-22 14:31:03 +01:00
total . count_restore = 0 ;
total . count_change = 0 ;
total . count_remove = 0 ;
total . count_insert = 0 ;
2011-06-20 20:35:04 +02:00
2015-02-22 14:31:03 +01:00
for ( i = scanlist ; i ! = 0 ; i = i - > next ) {
struct snapraid_scan * scan = i - > data ;
total . count_equal + = scan - > count_equal ;
total . count_move + = scan - > count_move ;
total . count_copy + = scan - > count_copy ;
2026-03-14 17:48:30 +01:00
total . count_relocate + = scan - > count_relocate ;
2015-02-22 14:31:03 +01:00
total . count_restore + = scan - > count_restore ;
total . count_change + = scan - > count_change ;
total . count_remove + = scan - > count_remove ;
total . count_insert + = scan - > count_insert ;
}
2013-09-30 22:24:30 +02:00
2015-02-22 14:31:03 +01:00
if ( is_diff ) {
msg_status ( " \n " ) ;
msg = msg_status ;
} else {
msg = msg_verbose ;
}
2013-09-30 22:24:30 +02:00
2015-02-22 14:31:03 +01:00
msg ( " %8u equal \n " , total . count_equal ) ;
2016-02-14 10:37:44 +01:00
msg ( " %8u added \n " , total . count_insert ) ;
msg ( " %8u removed \n " , total . count_remove ) ;
msg ( " %8u updated \n " , total . count_change ) ;
2015-02-22 14:31:03 +01:00
msg ( " %8u moved \n " , total . count_move ) ;
msg ( " %8u copied \n " , total . count_copy ) ;
2026-03-14 17:48:30 +01:00
msg ( " %8u relocated \n " , total . count_relocate ) ;
2015-02-22 14:31:03 +01:00
msg ( " %8u restored \n " , total . count_restore ) ;
2015-04-20 22:07:42 +02:00
log_tag ( " summary:equal:%u \n " , total . count_equal ) ;
2016-02-14 10:37:44 +01:00
log_tag ( " summary:added:%u \n " , total . count_insert ) ;
log_tag ( " summary:removed:%u \n " , total . count_remove ) ;
log_tag ( " summary:updated:%u \n " , total . count_change ) ;
2015-04-20 22:07:42 +02:00
log_tag ( " summary:moved:%u \n " , total . count_move ) ;
log_tag ( " summary:copied:%u \n " , total . count_copy ) ;
2026-03-14 17:48:30 +01:00
log_tag ( " summary:relocated:%u \n " , total . count_relocate ) ;
2015-04-20 22:07:42 +02:00
log_tag ( " summary:restored:%u \n " , total . count_restore ) ;
2026-02-09 15:50:58 +01:00
log_tag ( " list:scan_end \n " ) ;
2015-02-22 14:31:03 +01:00
2026-02-10 11:28:11 +01:00
/* save in the state */
state - > removed_files = total . count_remove ;
state - > updated_files = total . count_change ;
2026-03-14 17:48:30 +01:00
no_difference = ! total . count_move & & ! total . count_copy & & ! total . count_relocate & & ! total . count_restore
2015-02-22 14:31:03 +01:00
& & ! total . count_change & & ! total . count_remove & & ! total . count_insert ;
if ( is_diff ) {
2026-02-01 23:12:38 +01:00
if ( ! no_difference ) {
2015-02-22 14:31:03 +01:00
msg_status ( " There are differences! \n " ) ;
2026-02-01 23:12:38 +01:00
} else {
msg_status ( " No differences \n " ) ;
2011-06-20 20:35:04 +02:00
}
2026-02-02 16:32:16 +01:00
if ( state - > unsynced_blocks ! = 0 )
2026-02-01 23:12:38 +01:00
log_error ( EUSER , " The last sync was interrupted. Run it again! \n " ) ;
2011-03-19 00:34:01 +01:00
2026-02-10 23:15:49 +01:00
if ( state - > unsynced_blocks ! = 0 ) {
log_tag ( " summary:exit:unsynced \n " ) ;
} else if ( ! no_difference ) {
log_tag ( " summary:exit:diff \n " ) ;
} else {
log_tag ( " summary:exit:equal \n " ) ;
}
2015-02-22 14:31:03 +01:00
}
2026-02-10 23:15:49 +01:00
2015-04-20 22:07:42 +02:00
log_flush ( ) ;
2015-02-22 14:31:03 +01:00
2021-11-28 15:50:38 +01:00
tommy_list_foreach ( & scanlist , ( tommy_foreach_func * ) scan_free ) ;
2015-03-02 23:01:15 +01:00
2016-01-02 16:52:20 +01:00
/* check the file-system on all disks */
2015-10-05 21:40:44 +02:00
state_fscheck ( state , " after scan " ) ;
2015-03-02 23:01:15 +01:00
if ( is_diff ) {
/* check for file difference */
if ( ! no_difference )
2015-04-28 19:15:16 +02:00
return 1 ;
2015-03-02 23:01:15 +01:00
/* check also for incomplete "sync" */
2026-02-02 16:32:16 +01:00
if ( state - > unsynced_blocks ! = 0 )
2015-04-28 19:15:16 +02:00
return 1 ;
2015-03-02 23:01:15 +01:00
}
return 0 ;
}
int state_diff ( struct snapraid_state * state )
{
return state_diffscan ( state , 1 ) ;
}
void state_scan ( struct snapraid_state * state )
{
( void ) state_diffscan ( state , 0 ) ; /* ignore return value */
2011-03-04 20:59:21 +01:00
}