2011-03-04 20:59:21 +01:00
/*
* Copyright ( C ) 2011 Andrea Mazzoleni
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "portable.h"
# include "util.h"
# include "elem.h"
# include "state.h"
struct snapraid_scan {
2014-02-12 20:02:35 +01:00
struct snapraid_state * state ; /**< State used. */
struct snapraid_disk * disk ; /**< Disk used. */
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. */
unsigned count_copy ; /**< Files with same name, size and timestamp, in a different disk. */
2013-08-26 20:33:25 +02:00
unsigned count_restore ; /**< Files with equal name, size and timestamp, but different inode. */
unsigned count_change ; /**< Files modified. */
unsigned count_remove ; /**< Files removed. */
unsigned count_insert ; /**< Files new. */
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. */
2011-09-23 21:48:50 +02:00
/* nodes for data structures */
tommy_node node ;
2011-03-04 20:59:21 +01:00
} ;
2013-01-27 18:58:48 +01:00
/**
* Removes the specified link from the data set .
*/
2014-02-12 20:02:35 +01:00
static void scan_link_remove ( struct snapraid_scan * scan , struct snapraid_link * link )
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 ;
2013-01-27 18:58:48 +01:00
/* state changed */
state - > need_write = 1 ;
/* remove the file from the link containers */
tommy_hashdyn_remove_existing ( & disk - > linkset , & link - > nodeset ) ;
tommy_list_remove_existing ( & disk - > linklist , & link - > nodelist ) ;
/* deallocate */
link_free ( link ) ;
}
/**
* Inserts the specified link in the data set .
*/
2014-02-12 20:02:35 +01:00
static void scan_link_insert ( struct snapraid_scan * scan , struct snapraid_link * link )
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 ;
2013-01-27 18:58:48 +01:00
/* state changed */
state - > need_write = 1 ;
/* insert the link in the link containers */
tommy_hashdyn_insert ( & disk - > linkset , & link - > nodeset , link , link_name_hash ( link - > sub ) ) ;
tommy_list_insert_tail ( & disk - > linklist , & link - > nodelist , link ) ;
}
/**
* Processes a symbolic link .
*/
2014-02-12 20:02:35 +01:00
static void scan_link ( struct snapraid_scan * scan , int output , 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 ;
2013-01-27 18:58:48 +01:00
struct snapraid_link * link ;
/* check if the link already exists */
2013-10-19 13:24:35 +02:00
link = tommy_hashdyn_search ( & disk - > linkset , link_name_compare_to_arg , sub , link_name_hash ( sub ) ) ;
2013-01-27 18:58:48 +01:00
if ( link ) {
/* check if multiple files have the same name */
if ( link_flag_has ( link , FILE_IS_PRESENT ) ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2013-02-28 21:56:02 +01:00
fprintf ( stderr , " Internal inconsistency for link '%s%s' \n " , disk - > dir , sub ) ;
2013-01-27 18:58:48 +01:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-01-27 18:58:48 +01:00
}
/* mark as present */
link_flag_set ( link , FILE_IS_PRESENT ) ;
/* check if the link is not changed and it's of the same kind */
2013-02-28 21:56:02 +01:00
if ( strcmp ( link - > linkto , linkto ) = = 0 & & link_flag = = link_flag_get ( link , FILE_IS_LINK_MASK ) ) {
2013-01-27 18:58:48 +01:00
/* it's equal */
+ + scan - > count_equal ;
2013-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:equal:%s:%s \n " , disk - > name , link - > sub ) ;
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 */
state - > need_write = 1 ;
+ + scan - > count_change ;
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " scan:update:%s:%s \n " , disk - > name , link - > sub ) ;
2013-01-27 18:58:48 +01:00
if ( output ) {
2013-10-19 13:24:35 +02:00
printf ( " update %s%s \n " , disk - > dir , link - > sub ) ;
2013-01-27 18:58:48 +01:00
}
/* update it */
2013-05-30 23:50:26 +02:00
free ( link - > linkto ) ;
link - > linkto = strdup_nofail ( linkto ) ;
2013-02-28 21:56:02 +01:00
link_flag_let ( link , 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 ;
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " scan:add:%s:%s \n " , disk - > name , sub ) ;
2013-01-27 18:58:48 +01:00
if ( output ) {
2013-10-19 13:24:35 +02:00
printf ( " add %s%s \n " , disk - > dir , sub ) ;
2013-01-27 18:58:48 +01:00
}
/* and continue to insert it */
}
/* insert it */
2013-02-28 21:56:02 +01:00
link = link_alloc ( sub , linkto , link_flag ) ;
2013-01-27 18:58:48 +01:00
/* mark it as present */
link_flag_set ( link , FILE_IS_PRESENT ) ;
/* insert it in the delayed insert list */
tommy_list_insert_tail ( & scan - > link_insert_list , & link - > nodelist , link ) ;
}
2011-03-04 20:59:21 +01:00
/**
2013-12-24 21:46:27 +01:00
* Removes the specified file from the parity .
2011-03-04 20:59:21 +01:00
*/
2014-02-12 20:02:35 +01:00
static void scan_file_deallocate ( 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 ;
2011-09-23 21:48:50 +02:00
block_off_t i ;
2011-03-22 23:23:45 +01:00
/* state changed */
state - > need_write = 1 ;
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 ) {
2013-02-24 19:02:53 +01:00
struct snapraid_block * block = & file - > blockvec [ i ] ;
block_off_t block_pos = block - > parity_pos ;
unsigned block_state ;
struct snapraid_deleted * deleted ;
2011-03-04 20:59:21 +01:00
/* adjust the first free position */
2013-06-12 20:22:56 +02:00
/* note that doing all the deletions before alllocations, */
/* first_free_block is always 0 and the "if" is never triggered */
/* but we keep this code anyway for completeness. */
2011-03-04 20:59:21 +01:00
if ( disk - > first_free_block > block_pos )
disk - > first_free_block = block_pos ;
2014-02-01 23:32:53 +01:00
/* allocated a new deleted block from the block we are going to delete */
deleted = deleted_dup ( block ) ;
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) */
/* with the hash of CHG block not represeting the real parity state */
2014-02-01 23:32:53 +01:00
hash_invalid_set ( deleted - > 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 */
2014-02-01 23:32:53 +01:00
hash_invalid_set ( deleted - > 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 */
2014-06-25 23:23:22 +02:00
fprintf ( stderr , " Internal inconsistency in deallocating for block %u state %u \n " , block - > parity_pos , block_state ) ;
2013-02-24 19:02:53 +01:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-02-24 19:02:53 +01:00
}
/* insert it in the list of deleted blocks */
tommy_list_insert_tail ( & disk - > deletedlist , & deleted - > node , deleted ) ;
/* set the deleted block in the block array */
2013-09-28 09:40:08 +02:00
tommy_arrayblk_set ( & disk - > blockarr , block_pos , & deleted - > block ) ;
2011-03-04 20:59:21 +01:00
}
2013-12-24 21:46:27 +01:00
}
/**
2014-02-01 23:32:53 +01:00
* Checks 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
*/
2014-02-16 08:59:59 +01:00
static int file_is_full_invalid_parity_and_stable ( struct snapraid_state * state , 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 ) {
2013-12-24 21:46:27 +01:00
struct snapraid_block * block = & file - > blockvec [ i ] ;
2014-06-15 17:56:43 +02:00
snapraid_info info ;
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
2014-06-15 17:56:43 +02:00
/* get block specific info */
info = info_get ( & state - > infoarr , block - > parity_pos ) ;
2014-02-16 08:59:59 +01:00
2014-06-15 17:56:43 +02: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
/**
* Checks 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
*/
2014-02-16 08:59:59 +01:00
static int file_is_full_hashed_and_stable ( struct snapraid_state * state , 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
struct snapraid_block * block = & file - > blockvec [ i ] ;
snapraid_info info ;
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 ;
/* get block specific info */
info = info_get ( & state - > infoarr , block - > parity_pos ) ;
2014-02-16 08:59:59 +01:00
/* exclude blocks needing a rehash */
if ( info_get_rehash ( info ) )
2014-02-12 20:02:35 +01:00
return 0 ;
}
return 1 ;
}
2014-10-03 21:28:56 +02:00
static void scan_file_delayed_insert ( struct snapraid_scan * scan , struct snapraid_file * file )
{
struct snapraid_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
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
& & file - > physical = = FILEPHY_UNREAD_OFFSET
) {
2014-10-03 21:28:56 +02:00
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 */
fprintf ( stderr , " Error in getting the physical offset of file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
}
tommy_list_insert_tail ( & scan - > file_insert_list , & file - > nodelist , file ) ;
}
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 ,
* relly 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
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 */
fprintf ( stderr , " Error in stat file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
if ( st - > st_mtime ! = synced_st . st_mtime
| | st - > st_mtimensec ! = synced_st . st_mtimensec
) {
fprintf ( stderr , " WARNING! Detected uncached time change for file '%s' \n " , sub ) ;
fprintf ( stderr , " It's better if you run SnapRAID without other processes running. \n " ) ;
st - > st_mtime = synced_st . st_mtime ;
st - > st_mtimensec = synced_st . st_mtimensec ;
}
if ( st - > st_size ! = synced_st . st_size ) {
fprintf ( stderr , " WARNING! Detected uncached size change for file '%s' \n " , sub ) ;
fprintf ( stderr , " It's better if you run SnapRAID without other processes running. \n " ) ;
st - > st_size = synced_st . st_size ;
}
if ( st - > st_ino ! = synced_st . st_ino ) {
fprintf ( stderr , " DANGER! Detected uncached inode change for file '%s' \n " , sub ) ;
fprintf ( stderr , " Please ensure to run SnapRAID without other processes running. \n " ) ;
/* 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 ;
# endif
}
2013-12-24 21:46:27 +01:00
/**
* Keeps the file as it ' s ( or with only a name / inode modification ) .
2014-02-01 23:32:53 +01:00
*
2014-06-15 17:59:00 +02:00
* 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
2013-12-24 21:46:27 +01:00
* to always minimize the space used in the parity .
2014-02-01 23:32:53 +01:00
*
* This could happen after a failed sync , when some other files are deleted ,
2014-06-15 17:59:00 +02:00
* and then new ones can be moved backward to fill the hole created .
2013-12-24 21:46:27 +01:00
*/
2014-02-12 20:02:35 +01:00
static void scan_file_keep ( 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 ;
2014-06-15 17:59:00 +02:00
/* if the file is full invalid, schedule a reinsert at later stage */
if ( file_is_full_invalid_parity_and_stable ( scan - > state , file ) ) {
/* deallocate the file from the parity */
scan_file_deallocate ( scan , file ) ;
2013-12-24 21:46:27 +01:00
2014-06-15 17:59:00 +02:00
/* remove the file from the list */
tommy_list_remove_existing ( & disk - > filelist , & file - > nodelist ) ;
2013-12-24 21:46:27 +01:00
2014-06-15 17:59:00 +02:00
/* insert the file in the delayed block allocation */
2014-10-03 21:28:56 +02:00
scan_file_delayed_insert ( scan , file ) ;
2014-06-15 17:59:00 +02:00
}
2013-12-24 21:46:27 +01:00
}
/**
* Removes the specified file from the data set .
*/
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 ;
2013-12-24 21:46:27 +01:00
/* deallocate the file from the parity */
2014-02-12 20:02:35 +01:00
scan_file_deallocate ( scan , file ) ;
2011-03-04 20:59:21 +01:00
/* remove the file from the file containers */
2013-08-26 20:33:25 +02:00
if ( ! file_flag_has ( file , FILE_IS_WITHOUT_INODE ) ) {
tommy_hashdyn_remove_existing ( & disk - > inodeset , & file - > nodeset ) ;
}
2011-10-09 23:41:08 +02:00
tommy_hashdyn_remove_existing ( & disk - > pathset , & file - > pathset ) ;
2014-02-12 20:02:35 +01:00
tommy_hashdyn_remove_existing ( & disk - > stampset , & file - > stampset ) ;
2011-03-04 20:59:21 +01:00
tommy_list_remove_existing ( & disk - > filelist , & file - > nodelist ) ;
/* deallocate */
file_free ( file ) ;
}
/**
2011-03-16 23:52:10 +01:00
* Inserts the specified file in the data set .
2011-03-04 20:59:21 +01:00
*/
2014-02-12 20:02:35 +01:00
static void scan_file_insert ( 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 ;
2011-03-12 10:09:59 +01:00
block_off_t i ;
block_off_t block_max ;
block_off_t block_pos ;
2011-03-04 20:59:21 +01:00
2011-03-22 23:23:45 +01:00
/* state changed */
state - > need_write = 1 ;
2011-03-04 20:59:21 +01:00
/* allocate the blocks of the file */
block_pos = disk - > first_free_block ;
2013-09-28 09:40:08 +02:00
block_max = tommy_arrayblk_size ( & disk - > blockarr ) ;
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 ;
struct snapraid_block * block = & file - > blockvec [ i ] ;
2014-02-01 23:32:53 +01:00
/* increment the position until the first really free block */
2013-09-28 09:40:08 +02:00
while ( block_pos < block_max & & block_has_file ( tommy_arrayblk_get ( & disk - > blockarr , block_pos ) ) )
2011-03-04 20:59:21 +01:00
+ + block_pos ;
/* if not found, allocate a new one */
if ( block_pos = = block_max ) {
+ + block_max ;
2013-09-28 09:40:08 +02:00
tommy_arrayblk_grow ( & disk - > blockarr , block_max ) ;
2011-03-04 20:59:21 +01:00
}
2012-02-09 23:17:36 +01:00
/* set the position */
2014-02-12 20:02:35 +01:00
block - > parity_pos = block_pos ;
/* get block specific info */
info = info_get ( & state - > infoarr , block_pos ) ;
2012-02-09 23:17:36 +01:00
2014-02-12 20:02:35 +01:00
/* if the file block already has an updated hash without rehash */
if ( block_has_updated_hash ( block ) & & ! info_get_rehash ( info ) ) {
2014-02-01 23:32:53 +01:00
/* the only possible case is for REP blocks */
2014-02-12 20:02:35 +01:00
assert ( block_state_get ( block ) = = BLOCK_STATE_REP ) ;
2014-09-27 00:32:39 +02:00
2014-02-01 23:32:53 +01:00
/* convert to a REP block */
block_state_set ( & file - > blockvec [ i ] , BLOCK_STATE_REP ) ;
2014-02-01 17:57:26 +01:00
2014-02-01 23:32:53 +01:00
/* and keep the hash as it's */
2013-02-24 19:02:53 +01:00
} else {
2014-02-12 20:02:35 +01:00
struct snapraid_block * over_block ;
2014-02-01 23:32:53 +01:00
unsigned block_state ;
2013-09-29 21:01:31 +02:00
2014-02-01 23:32:53 +01:00
/* convert to a CHG block */
2014-02-12 20:02:35 +01:00
block_state_set ( block , BLOCK_STATE_CHG ) ;
2014-02-01 23:32:53 +01:00
/* block to overwrite */
2014-02-12 20:02:35 +01:00
over_block = tommy_arrayblk_get ( & disk - > blockarr , block_pos ) ;
2014-02-01 23:32:53 +01:00
/* state of the block we are going to overwrite */
2014-02-12 20:02:35 +01:00
block_state = block_state_get ( over_block ) ;
2014-02-01 23:32:53 +01:00
/* if the block is an empty one */
if ( block_state = = BLOCK_STATE_EMPTY ) {
/* the block was empty and filled with zeros */
/* set the hash to the special ZERO value */
2014-02-12 20:02:35 +01:00
hash_zero_set ( block - > hash ) ;
2014-02-01 23:32:53 +01:00
} else {
/* otherwise it's a DELETED one */
assert ( block_state = = BLOCK_STATE_DELETED ) ;
/* copy the past hash of the block */
2014-02-12 20:02:35 +01:00
memcpy ( block - > hash , over_block - > hash , HASH_SIZE ) ;
2014-02-01 23:32:53 +01:00
2014-06-13 20:52:03 +02:00
/* if we have not already cleared the past hash */
if ( ! state - > clear_past_hash ) {
2014-02-01 23:32:53 +01:00
/* 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) */
/* with the hash of DELETED block not represeting the real parity state */
2014-02-12 20:02:35 +01:00
hash_invalid_set ( block - > hash ) ;
2014-02-01 23:32:53 +01:00
}
}
2013-02-24 19:02:53 +01:00
}
2012-02-09 23:17:36 +01:00
2012-03-13 23:45:56 +01:00
/* store in the disk map, after invalidating all the other blocks */
2014-02-12 20:02:35 +01:00
tommy_arrayblk_set ( & disk - > blockarr , block_pos , block ) ;
2014-02-01 23:32:53 +01:00
/* set the new free position */
2011-03-04 20:59:21 +01:00
disk - > first_free_block = block_pos + 1 ;
}
2012-08-19 17:07:56 +02:00
/* note that the file is already added in the file hashtables */
2011-03-04 20:59:21 +01:00
tommy_list_insert_tail ( & disk - > filelist , & file - > nodelist , file ) ;
}
2011-03-16 23:52:10 +01:00
/**
* Processes a file .
*/
2014-10-04 10:27:23 +02:00
static void scan_file ( struct snapraid_scan * scan , int output , 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-03-02 21:56:59 +01:00
int add_insert ;
int add_change ;
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 ;
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
*
* For persistent inodes we mean inodes that keep their values when the filesystem
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 .
* As worse , the exFAT support in FUSE doesn ' t use subsecond precision in timestamps
* 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 .
* Otherwise , if the UUID is changed , likely it ' s a new recreated filesystem ,
* and then the inode have no meaning .
*
* Note that to disable the search by past inode , we do this implicitely
* 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 ) ) {
2014-09-30 21:54:49 +02:00
# if HAVE_STRUCT_STAT_ST_NLINK
if ( st - > st_nlink < = 1 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2014-09-27 00:32:39 +02:00
fprintf ( stderr , " Internal inode '% " PRIu64 " ' inconsistency for file '%s%s' already present \n " , ( uint64_t ) st - > st_ino , disk - > dir , sub ) ;
2013-08-26 20:33:25 +02:00
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-08-26 20:33:25 +02:00
}
2014-09-30 21:54:49 +02:00
# endif
/* it's a hardlink */
scan_link ( scan , output , sub , file - > sub , FILE_IS_HARDLINK ) ;
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 */
state - > need_write = 1 ;
}
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
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " scan:move:%s:%s:%s \n " , disk - > name , file - > sub , sub ) ;
2011-06-20 20:35:04 +02:00
if ( output ) {
2013-10-19 13:24:35 +02:00
printf ( " move %s%s -> %s%s \n " , disk - > dir , file - > sub , disk - > dir , sub ) ;
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 */
state - > 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
2013-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:equal:%s:%s \n " , disk - > name , file - > sub ) ;
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
}
/* here the file matches the inode, but not the other info */
/* if could be a modified file with the same name, */
/* or a restored/copied file that get assigned a previously used inode, */
/* or a filesystem with not persistent inodes */
/* 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 */
2014-09-27 00:32:39 +02:00
fprintf ( stderr , " Internal inode '% " PRIu64 " ' inconsistency for files '%s%s' and '%s%s' matching and already present but different \n " , file - > inode , disk - > dir , sub , disk - > dir , file - > sub ) ;
2013-08-26 20:33:25 +02:00
exit ( EXIT_FAILURE ) ;
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. */
2013-08-26 20:33:25 +02:00
/* If the file is found by name (not necessarely in this function call), */
2013-08-26 21:02:20 +02:00
/* 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-03-02 21:56:59 +01:00
add_insert = 0 ;
add_change = 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 ) ) ;
2013-08-26 20:33:25 +02:00
if ( file ) {
/* 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 */
2014-09-27 00:32:39 +02:00
fprintf ( stderr , " Internal inconsistency in inode '% " PRIu64 " ' for files '%s%s' as unexpected matching \n " , file - > inode , disk - > dir , sub ) ;
2013-08-26 20:33:25 +02:00
exit ( EXIT_FAILURE ) ;
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 */
2014-06-25 23:23:22 +02:00
fprintf ( stderr , " Internal inconsistency in path for file '%s%s' matching and already present \n " , disk - > dir , sub ) ;
2013-08-26 20:33:25 +02:00
exit ( EXIT_FAILURE ) ;
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 */
state - > 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
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " scan:restore:%s:%s \n " , disk - > name , sub ) ;
2013-09-30 22:42:33 +02:00
if ( output ) {
2013-10-19 13:24:35 +02:00
printf ( " restore %s%s \n " , disk - > dir , sub ) ;
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 */
state - > need_write = 1 ;
} 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 ;
if ( state - > opt . gui ) {
fprintf ( stdlog , " scan:equal:%s:%s \n " , disk - > name , file - > sub ) ;
}
}
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
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
2013-08-26 20:33:25 +02:00
/* it has the same name, so it's an update */
2014-03-02 21:56:59 +01:00
add_change = 1 ;
2011-06-24 20:47:22 +02:00
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " scan:update:%s:%s \n " , disk - > name , file - > sub ) ;
2013-08-26 20:33:25 +02:00
if ( output ) {
2014-03-02 21:56:59 +01:00
printf ( " update %s%s \n " , disk - > dir , file - > sub ) ;
2011-03-04 20:59:21 +01:00
}
2013-08-26 20:33:25 +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-10-04 10:27:23 +02:00
/* and continue to reinsert it again */
2012-02-03 19:16:14 +01:00
} else {
2013-08-26 20:33:25 +02:00
/* if the name doesn't exist, it's a new file */
2014-03-02 21:56:59 +01:00
add_insert = 1 ;
2012-02-03 19:16:14 +01:00
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " scan:add:%s:%s \n " , disk - > name , sub ) ;
2012-02-03 19:16:14 +01:00
if ( output ) {
2013-10-19 13:24:35 +02:00
printf ( " add %s%s \n " , disk - > dir , sub ) ;
2012-02-03 19:16:14 +01:00
}
/* and continue to insert it */
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, */
/* not that we refresh only the info of the new or modified files */
/* 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
/* 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 */
if ( is_original_file_size_different_than_zero & & st - > st_size = = 0 ) {
if ( ! state - > opt . force_zero ) {
/* LCOV_EXCL_START */
fprintf ( stderr , " The file '%s%s' has unexpected zero size! If this an expected state \n " , disk - > dir , sub ) ;
fprintf ( stderr , " you can '%s' anyway usinge 'snapraid --force-zero %s' \n " , state - > command , state - > command ) ;
fprintf ( stderr , " Instead, it's possible that after a kernel crash this file was lost, \n " ) ;
fprintf ( stderr , " and you can use 'snapraid --filter %s fix' to recover it. \n " , sub ) ;
exit ( EXIT_FAILURE ) ;
/* LCOV_EXCL_STOP */
}
}
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 */
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 ;
/* 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 ) ;
/* if found, and it's a fully hashed file */
if ( other_file & & file_is_full_hashed_and_stable ( scan - > state , other_file ) ) {
/* assume that the file is a copy, and reuse the hash */
file_copy ( other_file , file ) ;
/* revert old counter and use the copy one */
add_insert = 0 ;
add_change = 0 ;
+ + scan - > count_copy ;
fprintf ( stdlog , " scan:copy:%s:%s:%s:%s \n " , other_disk - > name , other_file - > sub , disk - > name , file - > sub ) ;
if ( output ) {
printf ( " copy %s%s -> %s%s \n " , other_disk - > dir , other_file - > sub , disk - > dir , file - > sub ) ;
}
2014-03-02 21:56:59 +01:00
2014-05-15 23:45:51 +02:00
/* no need to continue the search */
break ;
2014-02-12 20:02:35 +01:00
}
}
}
2014-03-02 21:56:59 +01:00
/* modify counter */
scan - > count_insert + = add_insert ;
scan - > count_change + = add_change ;
2012-08-19 17:07:56 +02:00
/* insert the file in the file hashtables, to allow to find duplicate hardlinks */
tommy_hashdyn_insert ( & disk - > inodeset , & file - > nodeset , file , file_inode_hash ( file - > inode ) ) ;
tommy_hashdyn_insert ( & disk - > pathset , & file - > pathset , file , file_path_hash ( file - > sub ) ) ;
2014-02-12 20:02:35 +01:00
tommy_hashdyn_insert ( & disk - > stampset , & file - > stampset , file , file_stamp_hash ( file - > size , file - > mtime_sec , file - > mtime_nsec ) ) ;
2012-08-19 17:07:56 +02:00
/* insert the file in the delayed block allocation */
2014-10-03 21:28:56 +02:00
scan_file_delayed_insert ( scan , file ) ;
2011-08-26 14:18:38 +02:00
}
2013-01-27 16:44:04 +01:00
/**
* Removes the specified dir from the data set .
*/
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_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
2013-01-27 16:44:04 +01:00
/* state changed */
state - > need_write = 1 ;
/* 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 ) ;
}
/**
* Inserts the specified dir in the data set .
*/
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_state * state = scan - > state ;
struct snapraid_disk * disk = scan - > disk ;
2013-01-27 16:44:04 +01:00
/* state changed */
state - > need_write = 1 ;
/* 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 ) ;
}
/**
* Processes a dir .
*/
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 */
2013-02-28 21:56:02 +01:00
fprintf ( stderr , " Internal inconsistency for dir '%s%s' \n " , disk - > dir , sub ) ;
2013-01-27 16:44:04 +01:00
exit ( EXIT_FAILURE ) ;
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
/**
* Returns the stat info of a dir entry .
*/
# 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
# define DSTAT(file, dd, buf) dstat(file, buf)
struct stat * dstat ( const char * file , struct stat * st )
{
if ( lstat ( file , st ) ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2013-10-02 23:05:22 +02:00
fprintf ( stderr , " Error in stat file/directory '%s'. %s. \n " , file , strerror ( errno ) ) ;
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
/**
* Processes 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
*/
2014-02-12 20:02:35 +01:00
static int scan_dir ( struct snapraid_scan * scan , int output , const char * dir , const char * sub )
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 ;
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 ;
tommy_list_init ( & list ) ;
2011-03-04 20:59:21 +01:00
d = opendir ( dir ) ;
if ( ! d ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2011-05-04 19:28:36 +02:00
fprintf ( stderr , " Error opening directory '%s'. %s. \n " , dir , strerror ( errno ) ) ;
2013-01-19 17:26:55 +01:00
fprintf ( stderr , " You can exclude it in the config file with: \n \t exclude /%s \n " , sub ) ;
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 ) {
2011-03-16 23:52:10 +01:00
char path_next [ PATH_MAX ] ;
char sub_next [ PATH_MAX ] ;
2013-10-02 23:05:22 +02:00
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
2012-11-03 23:57:12 +01:00
/* clear errno to detect erroneous conditions */
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 */
2011-05-04 19:28:36 +02:00
fprintf ( stderr , " Error reading directory '%s'. %s. \n " , dir , strerror ( errno ) ) ;
2013-01-19 17:26:55 +01:00
fprintf ( stderr , " You can exclude it in the config file with: \n \t exclude /%s \n " , sub ) ;
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
2011-06-26 21:37:47 +02:00
pathprint ( path_next , sizeof ( path_next ) , " %s%s " , dir , name ) ;
pathprint ( sub_next , sizeof ( sub_next ) , " %s%s " , sub , name ) ;
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 */
2011-05-04 19:28:36 +02:00
fprintf ( stderr , " Unsupported name '%s' in file '%s'. \n " , name , path_next ) ;
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 ) {
2013-06-29 18:11:36 +02:00
if ( state - > opt . verbose ) {
2013-01-18 00:15:55 +01:00
printf ( " 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 ) {
2013-06-29 18:11:36 +02:00
if ( state - > opt . verbose ) {
2013-01-18 00:15:55 +01:00
printf ( " Excluding content '%s' \n " , path_next ) ;
}
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 ) ;
}
if ( closedir ( d ) ! = 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2013-10-02 23:05:22 +02:00
fprintf ( stderr , " Error closing directory '%s'. %s. \n " , dir , strerror ( errno ) ) ;
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 ) {
2013-10-02 23:05:22 +02:00
char path_next [ PATH_MAX ] ;
char sub_next [ PATH_MAX ] ;
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
pathprint ( path_next , sizeof ( path_next ) , " %s%s " , dir , name ) ;
pathprint ( sub_next , sizeof ( sub_next ) , " %s%s " , sub , name ) ;
/* 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-04 10:27:23 +02:00
/* at now this can happen only in Windows, but 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-09-30 21:54:49 +02: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 */
fprintf ( stderr , " Error in stat file/directory '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
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 */
2013-01-18 00:15:55 +01:00
if ( filter_path ( & state - > filterlist , 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 */
if ( st - > st_ino = = 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 */
2014-10-04 10:27:23 +02:00
fprintf ( stderr , " 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-03 21:28:56 +02:00
scan_file ( scan , output , 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 {
2013-06-29 18:11:36 +02:00
if ( state - > opt . verbose ) {
2011-06-26 21:37:47 +02:00
printf ( " Excluding file '%s' \n " , path_next ) ;
2011-03-16 23:52:10 +01:00
}
}
2013-10-02 23:05:22 +02:00
} else if ( type = = 1 ) { /* LNK */
2013-01-18 00:15:55 +01:00
if ( filter_path ( & state - > filterlist , disk - > name , sub_next ) = = 0 ) {
2011-08-26 14:18:38 +02:00
char subnew [ PATH_MAX ] ;
int ret ;
ret = readlink ( path_next , subnew , sizeof ( subnew ) ) ;
if ( ret > = PATH_MAX ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2011-08-26 14:18:38 +02:00
fprintf ( stderr , " Error in readlink file '%s'. Symlink too long. \n " , path_next ) ;
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 */
2011-08-26 14:18:38 +02:00
fprintf ( stderr , " Error in readlink file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2011-08-26 14:18:38 +02:00
}
/* readlink doesn't put the final 0 */
subnew [ ret ] = 0 ;
2013-01-27 18:06:15 +01:00
/* process as a symbolic link */
2014-02-12 20:02:35 +01:00
scan_link ( scan , output , sub_next , subnew , FILE_IS_SYMLINK ) ;
2013-01-27 16:44:04 +01:00
processed = 1 ;
2011-08-26 14:18:38 +02:00
} else {
2013-06-29 18:11:36 +02:00
if ( state - > opt . verbose ) {
2013-01-18 00:15:55 +01:00
printf ( " Excluding link '%s' \n " , path_next ) ;
2011-08-26 14:18:38 +02:00
}
}
2013-10-02 23:05:22 +02:00
} else if ( type = = 2 ) { /* DIR */
2013-01-18 00:15:55 +01:00
if ( filter_dir ( & state - > filterlist , 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
2013-07-30 20:49:08 +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 ) {
2013-08-27 22:42:50 +02:00
fprintf ( stderr , " 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
char sub_dir [ PATH_MAX ] ;
/* recurse */
pathslash ( path_next , sizeof ( path_next ) ) ;
pathcpy ( sub_dir , sizeof ( sub_dir ) , sub_next ) ;
pathslash ( sub_dir , sizeof ( sub_dir ) ) ;
2014-02-12 20:02:35 +01:00
if ( scan_dir ( scan , output , path_next , sub_dir ) = = 0 ) {
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 {
2013-06-29 18:11:36 +02:00
if ( state - > opt . verbose ) {
2011-06-26 21:37:47 +02:00
printf ( " Excluding directory '%s' \n " , path_next ) ;
2011-03-16 23:52:10 +01:00
}
2011-03-04 20:59:21 +01:00
}
} else {
2013-01-18 00:15:55 +01:00
if ( filter_path ( & state - > filterlist , 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
2013-10-02 23:05:22 +02:00
fprintf ( stderr , " WARNING! Ignoring special '%s' file '%s' \n " , stat_desc ( st ) , path_next ) ;
2011-06-11 09:38:19 +02:00
} else {
2013-06-29 18:11:36 +02:00
if ( state - > opt . verbose ) {
2013-10-02 23:05:22 +02:00
printf ( " Excluding special file '%s' \n " , path_next ) ;
2011-06-11 09:38:19 +02:00
}
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
}
2011-06-20 20:35:04 +02:00
void state_scan ( struct snapraid_state * state , int output )
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 ;
2011-09-23 21:48:50 +02:00
tommy_list_init ( & scanlist ) ;
2011-03-04 20:59:21 +01:00
2013-10-19 13:24:35 +02:00
if ( output )
printf ( " Comparing... \n " ) ;
2014-02-12 20:02:35 +01:00
/* first scan all the directory and find new and deleted files */
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 ;
2011-03-04 20:59:21 +01:00
tommy_node * node ;
2013-08-27 23:02:12 +02:00
int ret ;
int has_persistent_inode ;
2011-03-04 20:59:21 +01:00
2011-09-23 21:48:50 +02:00
scan = malloc_nofail ( sizeof ( struct snapraid_scan ) ) ;
2014-02-12 20:02:35 +01:00
scan - > state = state ;
scan - > disk = disk ;
2011-09-23 21:48:50 +02:00
scan - > count_equal = 0 ;
2013-08-26 20:33:25 +02:00
scan - > count_move = 0 ;
2014-03-02 21:56:59 +01:00
scan - > count_copy = 0 ;
2013-08-26 20:33:25 +02:00
scan - > count_restore = 0 ;
2011-09-23 21:48:50 +02:00
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 ) ;
2013-01-27 16:44:04 +01:00
tommy_list_init ( & scan - > dir_insert_list ) ;
2011-09-23 21:48:50 +02:00
tommy_list_insert_tail ( & scanlist , & scan - > node , scan ) ;
2013-10-19 13:24:35 +02:00
if ( ! output )
printf ( " Scanning disk %s... \n " , disk - > name ) ;
2011-03-04 20:59:21 +01:00
2013-08-27 23:02:12 +02:00
/* check if the disk supports persistent inodes */
2014-10-13 23:47:25 +02:00
ret = fsinfo ( disk - > dir , & has_persistent_inode , 0 ) ;
2013-08-27 23:02:12 +02:00
if ( ret < 0 ) {
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_START */
2013-08-27 23:02:12 +02:00
fprintf ( stderr , " Error accessing disk '%s' to get filesystem info. %s. \n " , disk - > dir , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
2014-02-03 22:40:51 +01:00
/* LCOV_EXCL_STOP */
2013-08-27 23:02:12 +02:00
}
if ( ! has_persistent_inode ) {
2013-12-14 17:54:04 +01:00
disk - > has_volatile_inodes = 1 ;
}
2013-09-25 23:20:07 +02:00
2014-04-03 23:02:43 +02:00
/* if inodes or UUID are not persistent/changed/unsupported */
if ( disk - > has_volatile_inodes | | disk - > has_different_uuid | | disk - > has_unsupported_uuid ) {
2013-09-25 23:20:07 +02:00
/* removes 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 */
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 ) ;
}
2013-08-27 23:02:12 +02:00
}
2014-02-12 20:02:35 +01:00
scan_dir ( scan , output , disk - > dir , " " ) ;
}
/* 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_count ;
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 ) ) {
2011-09-23 21:48:50 +02:00
+ + scan - > count_remove ;
2011-06-20 20:35:04 +02:00
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " scan:remove:%s:%s \n " , disk - > name , file - > sub ) ;
2011-06-20 20:35:04 +02:00
if ( output ) {
2013-10-19 13:24:35 +02:00
printf ( " remove %s%s \n " , disk - > dir , file - > sub ) ;
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 ) {
struct snapraid_link * link = node - > data ;
/* next node */
node = node - > next ;
/* remove if not present */
if ( ! link_flag_has ( link , FILE_IS_PRESENT ) ) {
2011-09-23 21:48:50 +02:00
+ + scan - > count_remove ;
2011-08-26 14:18:38 +02:00
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " scan:remove:%s:%s \n " , disk - > name , link - > sub ) ;
2011-08-26 14:18:38 +02:00
if ( output ) {
2013-10-19 13:24:35 +02:00
printf ( " remove %s%s \n " , disk - > dir , link - > sub ) ;
2011-08-26 14:18:38 +02:00
}
2014-02-12 20:02:35 +01:00
scan_link_remove ( scan , link ) ;
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_count = 0 ;
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
/* files without offset are expected to have duplicates */
& & phy_last ! = FILEPHY_WITHOUT_OFFSET
) {
/* if verbose, prints the list of duplicates real offsets */
/* other cases are for offsets not supported, so we don't need to report them file by file */
if ( state - > opt . verbose & & phy_last > = FILEPHY_REAL_OFFSET ) {
2014-09-27 00:32:39 +02:00
fprintf ( stderr , " WARNING! Files '%s%s' and '%s%s' have 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 ;
+ + phy_count ;
}
2011-05-24 21:49:15 +02:00
/* next node */
node = node - > next ;
/* insert it */
2014-02-12 20:02:35 +01:00
scan_file_insert ( 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 ) {
struct snapraid_link * link = node - > data ;
/* next node */
node = node - > next ;
/* insert it */
2014-02-12 20:02:35 +01:00
scan_link_insert ( scan , link ) ;
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
}
/* checks for disks where all the previously existing files where removed */
2013-06-29 18:11:36 +02:00
if ( ! state - > opt . force_empty ) {
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
2013-08-26 20:33:25 +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 ;
2011-09-03 10:19:18 +02:00
fprintf ( stderr , " All the files previously present in disk '%s' at dir '%s' " , disk - > name , disk - > dir ) ;
} else {
fprintf ( stderr , " , disk '%s' at dir '%s' " , disk - > name , disk - > dir ) ;
}
2011-03-04 20:59:21 +01:00
}
}
2013-09-25 23:20:07 +02:00
if ( done ) {
2011-09-03 10:19:18 +02:00
fprintf ( stderr , " are now missing or rewritten! \n " ) ;
2013-08-26 20:33:25 +02:00
fprintf ( stderr , " This could happen when deleting all the files from a disk, \n " ) ;
fprintf ( stderr , " and restoring them with a program not setting correctly the timestamps. \n " ) ;
fprintf ( stderr , " If this is really what you are doing, you can '%s' anyway, \n " , state - > command ) ;
fprintf ( stderr , " using 'snapraid --force-empty %s'. \n " , state - > command ) ;
2013-03-03 22:39:46 +01:00
fprintf ( stderr , " Instead, it's possible that you have some disks not mounted. \n " ) ;
2011-09-03 10:19:18 +02:00
exit ( EXIT_FAILURE ) ;
}
2011-09-03 10:12:13 +02:00
}
2011-03-04 20:59:21 +01:00
2013-09-25 23:20:07 +02:00
/* checks for disks without the physical offset support */
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 ;
fprintf ( stderr , " WARNING! Physical offsets not supported for disk '%s' " , disk - > name ) ;
} else {
2014-06-02 17:10:29 +02:00
fprintf ( stderr , " , '%s' " , disk - > name ) ;
2013-09-25 23:20:07 +02:00
}
}
}
if ( done ) {
2014-06-02 17:15:52 +02:00
fprintf ( stderr , " . Files order won't be optimal. \n " ) ;
2013-09-25 23:20:07 +02:00
}
}
2014-04-03 23:02:43 +02:00
/* checks for disks without persistent inodes */
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 ;
fprintf ( stderr , " WARNING! Inodes are not persistent for disks: '%s' " , disk - > name ) ;
} else {
2014-06-02 17:10:29 +02:00
fprintf ( stderr , " , '%s' " , disk - > name ) ;
2013-12-14 17:54:04 +01:00
}
}
2014-04-03 23:02:43 +02:00
}
if ( done ) {
2014-06-02 17:15:52 +02:00
fprintf ( stderr , " . Move operations won't be optimal. \n " ) ;
2013-12-14 17:54:04 +01:00
}
2014-04-03 23:02:43 +02:00
/* checks for disks with changed UUID */
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 ;
fprintf ( stderr , " WARNING! UUID is changed for disks: '%s' " , disk - > name ) ;
} else {
2014-06-02 17:10:29 +02:00
fprintf ( stderr , " , '%s' " , disk - > name ) ;
2013-09-25 23:20:07 +02:00
}
}
2014-04-03 23:02:43 +02:00
}
if ( done ) {
2014-04-05 16:04:53 +02:00
fprintf ( stderr , " . Move operations won't be optimal. \n " ) ;
2014-04-03 23:02:43 +02:00
}
/* checks for disks with unsupported UUID */
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 ;
fprintf ( stderr , " WARNING! UUID is unsupported for disks: '%s' " , disk - > name ) ;
} else {
2014-06-02 17:10:29 +02:00
fprintf ( stderr , " , '%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 ) {
2014-04-05 16:04:53 +02:00
fprintf ( stderr , " . Move operations won't be optimal. \n " ) ;
2014-04-03 23:02:43 +02:00
}
2013-09-25 23:20:07 +02:00
2013-06-29 18:11:36 +02:00
if ( state - > opt . verbose | | output ) {
2011-03-04 20:59:21 +01:00
struct snapraid_scan total ;
2013-09-30 22:24:30 +02:00
int no_difference ;
2011-03-04 20:59:21 +01:00
total . count_equal = 0 ;
2013-08-26 20:33:25 +02:00
total . count_move = 0 ;
2014-03-02 21:56:59 +01:00
total . count_copy = 0 ;
2013-08-26 20:33:25 +02:00
total . count_restore = 0 ;
2011-03-04 20:59:21 +01:00
total . count_change = 0 ;
total . count_remove = 0 ;
total . count_insert = 0 ;
2014-09-27 00:32:39 +02:00
for ( i = scanlist ; i ! = 0 ; i = i - > next ) {
2011-09-23 21:48:50 +02:00
struct snapraid_scan * scan = i - > data ;
total . count_equal + = scan - > count_equal ;
2013-08-26 20:33:25 +02:00
total . count_move + = scan - > count_move ;
2014-03-02 21:56:59 +01:00
total . count_copy + = scan - > count_copy ;
2013-08-26 20:33:25 +02:00
total . count_restore + = scan - > count_restore ;
2011-09-23 21:48:50 +02:00
total . count_change + = scan - > count_change ;
total . count_remove + = scan - > count_remove ;
total . count_insert + = scan - > count_insert ;
2011-03-04 20:59:21 +01:00
}
2013-10-19 13:24:35 +02:00
if ( state - > opt . verbose | | output ) {
if ( output )
printf ( " \n " ) ;
printf ( " %8u equal \n " , total . count_equal ) ;
printf ( " %8u moved \n " , total . count_move ) ;
2014-03-02 21:56:59 +01:00
printf ( " %8u copied \n " , total . count_copy ) ;
2013-10-19 13:24:35 +02:00
printf ( " %8u restored \n " , total . count_restore ) ;
2014-03-02 21:56:59 +01:00
printf ( " %8u updated \n " , total . count_change ) ;
2013-10-19 13:24:35 +02:00
printf ( " %8u removed \n " , total . count_remove ) ;
printf ( " %8u added \n " , total . count_insert ) ;
2011-06-20 20:35:04 +02:00
}
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " summary:equal:%u \n " , total . count_equal ) ;
fprintf ( stdlog , " summary:moved:%u \n " , total . count_move ) ;
2014-03-02 21:56:59 +01:00
fprintf ( stdlog , " summary:copied:%u \n " , total . count_copy ) ;
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " summary:restored:%u \n " , total . count_restore ) ;
2014-03-02 21:56:59 +01:00
fprintf ( stdlog , " summary:updated:%u \n " , total . count_change ) ;
2013-10-03 22:44:33 +02:00
fprintf ( stdlog , " summary:removed:%u \n " , total . count_remove ) ;
fprintf ( stdlog , " summary:added:%u \n " , total . count_insert ) ;
2013-09-30 22:24:30 +02:00
2014-03-02 21:56:59 +01:00
no_difference = ! total . count_move & & ! total . count_copy & & ! total . count_restore
& & ! total . count_change & & ! total . count_remove & & ! total . count_insert ;
2013-09-30 22:24:30 +02:00
2011-06-20 20:35:04 +02:00
if ( output ) {
2013-09-30 22:24:30 +02:00
if ( no_difference ) {
2013-10-19 13:24:35 +02:00
printf ( " No differences \n " ) ;
2013-09-30 22:24:30 +02:00
} else {
2013-10-19 13:24:35 +02:00
printf ( " There are differences! \n " ) ;
2013-09-30 22:24:30 +02:00
}
}
2013-10-03 22:44:33 +02:00
if ( no_difference ) {
fprintf ( stdlog , " summary:exit:equal \n " ) ;
} else {
fprintf ( stdlog , " summary:exit:diff \n " ) ;
2011-06-20 20:35:04 +02:00
}
2013-10-03 22:44:33 +02:00
fflush ( stdlog ) ;
2011-03-04 20:59:21 +01:00
}
2011-03-19 00:34:01 +01:00
2011-09-23 21:48:50 +02:00
tommy_list_foreach ( & scanlist , ( tommy_foreach_func * ) free ) ;
2011-03-04 20:59:21 +01:00
}