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 {
2011-05-24 21:49:15 +02:00
/**
* Counters of changes .
*/
2013-08-26 20:33:25 +02:00
unsigned count_equal ; /**< Files equal. */
unsigned count_move ; /**< Files with a different name, but equal inode, size and timestamp. */
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 .
*/
static void scan_link_remove ( struct snapraid_state * state , struct snapraid_disk * disk , struct snapraid_link * link )
{
/* 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 .
*/
static void scan_link_insert ( struct snapraid_state * state , struct snapraid_disk * disk , struct snapraid_link * link )
{
/* 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 .
*/
2013-02-28 21:56:02 +01:00
static void scan_link ( struct snapraid_scan * scan , struct snapraid_state * state , int output , struct snapraid_disk * disk , const char * sub , const char * linkto , unsigned link_flag )
2013-01-27 18:58:48 +01:00
{
struct snapraid_link * link ;
/* check if the link already exists */
link = tommy_hashdyn_search ( & disk - > linkset , link_name_compare , sub , link_name_hash ( sub ) ) ;
if ( link ) {
/* check if multiple files have the same name */
if ( link_flag_has ( link , FILE_IS_PRESENT ) ) {
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 ) ;
}
/* 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 ) ;
fflush ( stdlog ) ;
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-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:update:%s:%s \n " , disk - > name , link - > sub ) ;
fflush ( stdlog ) ;
2013-01-27 18:58:48 +01:00
}
if ( output ) {
printf ( " Update '%s%s' \n " , disk - > dir , link - > sub ) ;
}
/* 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-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:add:%s:%s \n " , disk - > name , sub ) ;
fflush ( stdlog ) ;
2013-01-27 18:58:48 +01:00
}
if ( output ) {
printf ( " Add '%s%s' \n " , disk - > dir , sub ) ;
}
/* 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
/**
2011-03-16 23:52:10 +01:00
* Removes the specified file from the data set .
2011-03-04 20:59:21 +01:00
*/
static void scan_file_remove ( struct snapraid_state * state , struct snapraid_disk * disk , struct snapraid_file * file )
{
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 */
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 ;
2013-02-24 19:02:53 +01:00
/* in case we scan after an aborted sync, */
/* we could get also intermediate states like inv/chg/new */
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 ;
case BLOCK_STATE_CHG :
case BLOCK_STATE_NEW :
/* 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 */
/* may be or may be not have been updated with the new data */
2013-08-21 22:34:22 +02:00
/* Then we reset the hash to a bogus value */
2013-02-24 19:02:53 +01:00
/* Note that this condition is possible only if: */
/* - new files added/modified */
/* - aborted sync, without saving the content file */
/* - files deleted after the aborted sync */
memset ( block - > hash , 0 , HASH_SIZE ) ;
break ;
default :
fprintf ( stderr , " Internal state inconsistency in scanning for block %u state %u \n " , block - > parity_pos , block_state ) ;
exit ( EXIT_FAILURE ) ;
}
/* allocated a new deleted block from the block we are going to delete */
deleted = deleted_dup ( block ) ;
/* 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
}
/* 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 ) ;
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
*/
static void scan_file_insert ( struct snapraid_state * state , struct snapraid_disk * disk , struct snapraid_file * file )
{
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 ) ;
2011-03-04 20:59:21 +01:00
for ( i = 0 ; i < file - > blockmax ; + + i ) {
2013-02-24 19:02:53 +01:00
struct snapraid_block * block ;
2012-03-13 23:45:56 +01:00
2011-03-04 20:59:21 +01:00
/* find a 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 */
2011-03-04 20:59:21 +01:00
file - > blockvec [ i ] . parity_pos = block_pos ;
2012-02-09 23:17:36 +01:00
2013-02-24 19:02:53 +01:00
/* block to overwrite */
2013-09-28 09:40:08 +02:00
block = tommy_arrayblk_get ( & disk - > blockarr , block_pos ) ;
2013-02-24 19:02:53 +01:00
/* if the block is an empty one */
if ( block = = BLOCK_EMPTY ) {
/* we just overwrite it with a NEW one */
2012-02-09 23:17:36 +01:00
block_state_set ( & file - > blockvec [ i ] , BLOCK_STATE_NEW ) ;
2013-02-24 19:02:53 +01:00
} else {
/* otherwise it's a DELETED one, that we convert in CHG keeping the hash */
2012-02-09 23:17:36 +01:00
block_state_set ( & file - > blockvec [ i ] , BLOCK_STATE_CHG ) ;
2013-02-24 19:02:53 +01:00
memcpy ( file - > blockvec [ i ] . hash , block - > hash , HASH_SIZE ) ;
}
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 */
2013-09-28 09:40:08 +02:00
tommy_arrayblk_set ( & disk - > blockarr , block_pos , & file - > blockvec [ i ] ) ;
2011-03-04 20:59:21 +01:00
}
if ( file - > blockmax ) {
/* set the new free position, but only if allocated something */
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 .
*/
2013-06-25 22:41:10 +02:00
static void scan_file ( struct snapraid_scan * scan , struct snapraid_state * state , int output , struct snapraid_disk * disk , const char * sub , const struct stat * st , uint64_t physical )
2011-03-04 20:59:21 +01:00
{
struct snapraid_file * file ;
2013-09-25 23:20:07 +02:00
uint64_t inode ;
2011-03-04 20:59:21 +01:00
2013-08-27 23:02:12 +02:00
file = 0 ;
/*
2013-09-25 23:20:07 +02:00
* If the disk has persistent inodes , try a search by inode ,
* 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
* file recognizition .
*
* We do this implicitely removing all the inode before searching for files .
* This ensure that no file is found with an old inode , but at the same time ,
* it allows to find new files with the same inode , and to identify them as hardlinks .
2013-08-27 23:02:12 +02:00
*/
2013-09-25 23:20:07 +02:00
inode = st - > st_ino ;
file = tommy_hashdyn_search ( & disk - > inodeset , file_inode_compare_to_arg , & inode , file_inode_hash ( inode ) ) ;
/* identify moved files searching by inode */
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 )
/* 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
)
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 ) ) {
if ( st - > st_nlink > 1 ) {
/* it's a hardlink */
scan_link ( scan , state , output , disk , sub , file - > sub , FILE_IS_HARDLINK ) ;
return ;
} else {
fprintf ( stderr , " Internal inode '% " PRIu64 " ' inconsistency for file '%s%s' already present \n " , ( uint64_t ) st - > st_ino , disk - > dir , sub ) ;
exit ( EXIT_FAILURE ) ;
}
}
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
2013-08-26 20:33:25 +02:00
/* update the nano seconds mtime only if different */
/* to avoid unneeded updates */
2013-08-18 18:02:25 +02:00
if ( file - > mtime_nsec = = STAT_NSEC_INVALID
& & STAT_NSEC ( st ) ! = STAT_NSEC_INVALID
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-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:move:%s:%s:%s \n " , disk - > name , file - > sub , sub ) ;
fflush ( stdlog ) ;
2011-06-20 20:35:04 +02:00
}
if ( output ) {
2011-06-26 21:37:47 +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 ) ;
fflush ( stdlog ) ;
2011-06-20 20:35:04 +02:00
}
2011-03-29 23:55:47 +02: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 ) ) {
2013-08-26 21:02:20 +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 ) ;
}
/* 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 */
}
/* then try findind it by name */
file = tommy_hashdyn_search ( & disk - > pathset , file_path_compare , sub , file_path_hash ( sub ) ) ;
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 ) {
fprintf ( stderr , " Internal inode '% " PRIu64 " ' inconsistency for files '%s%s' as unexpected matching \n " , file - > inode , disk - > dir , sub ) ;
2013-08-26 20:50:15 +02:00
2013-08-26 20:33:25 +02:00
exit ( EXIT_FAILURE ) ;
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 ) ) {
fprintf ( stderr , " Internal path inconsistency for file '%s%s' matching and already present \n " , disk - > dir , sub ) ;
exit ( EXIT_FAILURE ) ;
}
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 )
/* 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
)
) {
/* here we know that the inode is different and it needs to be updated */
2013-08-26 21:02:20 +02:00
/* otherwise we would have processed the file when searching by inode */
2013-08-26 20:33:25 +02:00
state - > need_write = 1 ;
2011-07-27 18:51:07 +02:00
2013-08-26 20:33:25 +02:00
/* mark as present */
file_flag_set ( file , FILE_IS_PRESENT ) ;
/* always update the nano seconds mtime */
/* to manage the above STAT_NSEC_INVALID case */
file - > mtime_nsec = STAT_NSEC ( st ) ;
/* if the inode is different, it means a rewritten file with the same path */
2013-08-26 21:02:20 +02:00
/* like when restoring a backup that restores also the timestamp */
2013-08-26 20:33:25 +02:00
+ + scan - > count_restore ;
if ( state - > opt . gui ) {
fprintf ( stdlog , " scan:restore:%s:%s \n " , disk - > name , sub ) ;
fflush ( stdlog ) ;
}
if ( output ) {
printf ( " Restore '%s%s' \n " , disk - > dir , sub ) ;
}
/* 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 ) ) ;
/* nothing more to do */
return ;
}
/* here if the file is changed but with the correct name */
/* 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 ( file - > size ! = 0 & & st - > st_size = = 0 ) {
if ( ! state - > opt . force_zero ) {
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 ) ;
2011-06-20 20:35:04 +02:00
}
2013-08-26 20:33:25 +02:00
}
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 */
+ + scan - > count_change ;
2011-06-24 20:47:22 +02:00
2013-08-26 20:33:25 +02:00
if ( state - > opt . gui ) {
fprintf ( stdlog , " scan:update:%s:%s \n " , disk - > name , file - > sub ) ;
fflush ( stdlog ) ;
}
if ( output ) {
if ( file - > size ! = st - > st_size )
printf ( " Update '%s%s' new size \n " , disk - > dir , file - > sub ) ;
else
printf ( " Update '%s%s' new modification time \n " , disk - > dir , file - > sub ) ;
2011-03-04 20:59:21 +01:00
}
2013-08-26 20:33:25 +02:00
/* remove it */
scan_file_remove ( state , disk , file ) ;
/* and continue to reinsert it */
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 */
2012-02-03 19:16:14 +01:00
+ + scan - > count_insert ;
2013-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:add:%s:%s \n " , disk - > name , sub ) ;
fflush ( stdlog ) ;
2012-02-03 19:16:14 +01:00
}
if ( output ) {
printf ( " Add '%s%s' \n " , disk - > dir , sub ) ;
}
/* and continue to insert it */
2011-03-04 20:59:21 +01: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
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 ) ) ;
/* insert the file in the delayed block allocation */
2011-08-26 14:18:38 +02:00
tommy_list_insert_tail ( & scan - > file_insert_list , & file - > nodelist , file ) ;
}
2013-01-27 16:44:04 +01:00
/**
* Removes the specified dir from the data set .
*/
static void scan_emptydir_remove ( struct snapraid_state * state , struct snapraid_disk * disk , struct snapraid_dir * dir )
{
/* 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 .
*/
static void scan_emptydir_insert ( struct snapraid_state * state , struct snapraid_disk * disk , struct snapraid_dir * dir )
{
/* 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 .
*/
static void scan_emptydir ( struct snapraid_scan * scan , struct snapraid_state * state , int output , struct snapraid_disk * disk , const char * sub )
{
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 ) ) {
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 ) ;
}
/* mark as present */
dir_flag_set ( dir , FILE_IS_PRESENT ) ;
/* 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 , dir - > sub ) ;
fflush ( stdlog ) ;
2013-01-27 16:44:04 +01:00
}
/* nothing more to do */
return ;
} else {
/* create the new dir */
+ + scan - > count_insert ;
2013-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:add:%s:%s \n " , disk - > name , sub ) ;
fflush ( stdlog ) ;
2013-01-27 16:44:04 +01:00
}
if ( output ) {
printf ( " Add '%s%s' \n " , disk - > dir , sub ) ;
}
/* 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 ) ;
}
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
*/
2013-01-27 16:44:04 +01:00
static int scan_dir ( struct snapraid_scan * scan , struct snapraid_state * state , int output , struct snapraid_disk * disk , const char * dir , const char * sub )
2011-03-04 20:59:21 +01:00
{
2013-01-27 16:44:04 +01:00
int processed = 0 ;
2011-03-04 20:59:21 +01:00
DIR * d ;
d = opendir ( dir ) ;
if ( ! d ) {
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 ) ;
}
2011-05-04 19:28:36 +02:00
while ( 1 ) {
2011-03-16 23:52:10 +01:00
char path_next [ PATH_MAX ] ;
char sub_next [ PATH_MAX ] ;
2011-03-12 10:09:59 +01:00
struct stat st ;
2011-05-04 19:28:36 +02:00
const char * name ;
struct dirent * dd ;
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 ) {
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 ) ;
}
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 ) ;
2011-05-04 19:28:36 +02:00
/* check for not supported file names, limitation derived from the content file format */
2011-05-24 21:03:30 +02:00
if ( name [ 0 ] = = 0 | | strchr ( name , ' \n ' ) ! = 0 | | name [ strlen ( name ) - 1 ] = = ' \r ' ) {
2011-05-04 19:28:36 +02:00
fprintf ( stderr , " Unsupported name '%s' in file '%s'. \n " , name , path_next ) ;
exit ( EXIT_FAILURE ) ;
}
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 ;
}
# if HAVE_DIRENT_LSTAT
/* convert dirent to lstat result */
dirent_lstat ( dd , & st ) ;
# else
/* get lstat info about the file */
2011-06-11 10:01:08 +02:00
if ( lstat ( path_next , & st ) ! = 0 ) {
2011-05-04 19:28:36 +02:00
fprintf ( stderr , " Error in stat file/directory '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
2011-03-22 23:23:45 +01:00
exit ( EXIT_FAILURE ) ;
}
2013-01-18 00:15:55 +01:00
# endif
2011-03-22 23:23:45 +01:00
2011-03-12 10:09:59 +01:00
if ( S_ISREG ( st . st_mode ) ) {
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
uint64_t physical ;
2011-09-20 22:35:15 +02:00
# if HAVE_LSTAT_EX
2011-05-04 19:28:36 +02:00
/* get inode info about the file, Windows needs an additional step */
2013-05-30 23:50:26 +02:00
/* also for hardlink, the real size of the file is read here */
2011-09-20 22:35:15 +02:00
if ( lstat_ex ( path_next , & st ) ! = 0 ) {
2011-05-04 19:28:36 +02:00
fprintf ( stderr , " Error in stat_inode file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
# endif
2013-06-29 18:11:36 +02:00
if ( state - > opt . force_order = = SORT_PHYSICAL ) {
2013-06-25 22:41:10 +02:00
if ( filephy ( path_next , & st , & physical ) ! = 0 ) {
fprintf ( stderr , " Error in getting the physical offset of file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
} else {
physical = 0 ;
}
2011-05-04 19:28:36 +02:00
2013-06-25 22:41:10 +02:00
scan_file ( scan , state , output , disk , sub_next , & st , physical ) ;
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
}
}
2011-08-26 14:18:38 +02:00
} else if ( S_ISLNK ( st . st_mode ) ) {
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 ) {
fprintf ( stderr , " Error in readlink file '%s'. Symlink too long. \n " , path_next ) ;
exit ( EXIT_FAILURE ) ;
}
if ( ret < 0 ) {
fprintf ( stderr , " Error in readlink file '%s'. %s. \n " , path_next , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
/* readlink doesn't put the final 0 */
subnew [ ret ] = 0 ;
2013-01-27 18:06:15 +01:00
/* process as a symbolic link */
2013-02-28 21:56:02 +01:00
scan_link ( scan , state , output , disk , 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
}
}
2011-03-12 10:09:59 +01:00
} else if ( S_ISDIR ( st . st_mode ) ) {
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
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-08-23 18:25:28 +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 ) ) ;
if ( scan_dir ( scan , state , output , disk , path_next , sub_dir ) = = 0 ) {
/* scan the directory as empty dir */
scan_emptydir ( scan , state , output , disk , sub_next ) ;
}
/* 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 ) {
2013-08-27 22:42:50 +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 ) {
2012-04-11 22:03:14 +02:00
printf ( " Excluding special '%s' file '%s' \n " , stat_desc ( & st ) , path_next ) ;
2011-06-11 09:38:19 +02:00
}
2011-03-04 20:59:21 +01:00
}
}
}
2011-05-04 19:28:36 +02:00
if ( closedir ( d ) ! = 0 ) {
fprintf ( stderr , " Error closing directory '%s'. %s. \n " , dir , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
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 ;
tommy_list_init ( & scanlist ) ;
2011-03-04 20:59:21 +01:00
2011-09-23 21:48:50 +02:00
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
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 ;
2013-09-25 23:20:07 +02:00
unsigned phy_count ;
unsigned phy_dup ;
uint64_t phy_last ;
2011-03-04 20:59:21 +01:00
2011-09-23 21:48:50 +02:00
scan = malloc_nofail ( sizeof ( struct snapraid_scan ) ) ;
scan - > count_equal = 0 ;
2013-08-26 20:33:25 +02:00
scan - > count_move = 0 ;
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 ) ;
2011-03-04 20:59:21 +01:00
printf ( " Scanning disk %s... \n " , disk - > name ) ;
2013-08-27 23:02:12 +02:00
/* check if the disk supports persistent inodes */
ret = fsinfo ( disk - > dir , & has_persistent_inode ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error accessing disk '%s' to get filesystem info. %s. \n " , disk - > dir , strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
if ( ! has_persistent_inode ) {
2013-09-25 23:20:07 +02:00
/* mask the disk */
2013-08-27 23:02:12 +02:00
disk - > has_not_persistent_inodes = 1 ;
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
}
2011-09-23 21:48:50 +02:00
scan_dir ( scan , state , output , disk , disk - > dir , " " ) ;
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-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:remove:%s:%s \n " , disk - > name , file - > sub ) ;
fflush ( stdlog ) ;
2011-06-20 20:35:04 +02:00
}
if ( output ) {
2011-06-26 21:37:47 +02:00
printf ( " Remove '%s%s' \n " , disk - > dir , file - > sub ) ;
2011-06-20 20:35:04 +02:00
}
scan_file_remove ( state , disk , 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-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:remove:%s:%s \n " , disk - > name , link - > sub ) ;
fflush ( stdlog ) ;
2011-08-26 14:18:38 +02:00
}
if ( output ) {
printf ( " Remove '%s%s' \n " , disk - > dir , link - > sub ) ;
}
scan_link_remove ( state , disk , link ) ;
}
}
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 ) ) {
+ + scan - > count_remove ;
2013-06-29 18:11:36 +02:00
if ( state - > opt . gui ) {
2013-03-01 20:13:56 +01:00
fprintf ( stdlog , " scan:remove:%s:%s \n " , disk - > name , dir - > sub ) ;
fflush ( stdlog ) ;
2013-01-27 16:44:04 +01:00
}
if ( output ) {
printf ( " Remove '%s%s' \n " , disk - > dir , dir - > sub ) ;
}
scan_emptydir_remove ( state , disk , dir ) ;
}
}
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 :
tommy_list_sort ( & scan - > file_insert_list , file_alpha_compare ) ;
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 */
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 ;
phy_last = - 1 ;
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 ) {
if ( phy_count > 0 & & file - > physical = = phy_last )
+ + phy_dup ;
phy_last = file - > physical ;
+ + phy_count ;
}
2011-05-24 21:49:15 +02:00
/* next node */
node = node - > next ;
/* insert it */
scan_file_insert ( state , disk , file ) ;
}
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 ) {
disk - > has_not_reliable_physical = 1 ;
}
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 */
scan_link_insert ( state , disk , link ) ;
}
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 */
scan_emptydir_insert ( state , disk , dir ) ;
}
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 ) {
2013-09-25 23:20:07 +02:00
int done = 0 ;
2011-09-23 21:48:50 +02:00
for ( i = state - > disklist , j = scanlist ; i ! = 0 ; i = i - > next , j = j - > next ) {
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 ) {
int done = 0 ;
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
struct snapraid_disk * disk = i - > data ;
if ( disk - > has_not_reliable_physical ) {
if ( ! done ) {
done = 1 ;
fprintf ( stderr , " WARNING! Physical offsets not supported for disk '%s' " , disk - > name ) ;
} else {
fprintf ( stderr , " , '%s " , disk - > name ) ;
}
}
}
if ( done ) {
fprintf ( stderr , " . Performance won't be optimal. \n " ) ;
}
}
/* Check for disks without persisten inodes */
{
int done = 0 ;
for ( i = state - > disklist ; i ! = 0 ; i = i - > next ) {
struct snapraid_disk * disk = i - > data ;
if ( disk - > has_not_persistent_inodes ) {
if ( ! done ) {
done = 1 ;
fprintf ( stderr , " WARNING! Inodes are not persistent for disk '%s' " , disk - > name ) ;
} else {
fprintf ( stderr , " , '%s " , disk - > name ) ;
}
}
}
if ( done ) {
fprintf ( stderr , " . Move operations won't be optimized. \n " ) ;
}
}
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 ;
total . count_equal = 0 ;
2013-08-26 20:33:25 +02:00
total . count_move = 0 ;
total . count_restore = 0 ;
2011-03-04 20:59:21 +01:00
total . count_change = 0 ;
total . count_remove = 0 ;
total . count_insert = 0 ;
2011-09-23 21:48:50 +02:00
for ( i = scanlist ; i ! = 0 ; i = i - > next ) {
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 ;
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-06-29 18:11:36 +02:00
if ( state - > opt . verbose ) {
2011-06-20 20:35:04 +02:00
printf ( " \t equal %d \n " , total . count_equal ) ;
2013-08-26 20:33:25 +02:00
printf ( " \t moved %d \n " , total . count_move ) ;
printf ( " \t restored %d \n " , total . count_restore ) ;
2011-06-20 20:35:04 +02:00
printf ( " \t changed %d \n " , total . count_change ) ;
printf ( " \t removed %d \n " , total . count_remove ) ;
printf ( " \t added %d \n " , total . count_insert ) ;
}
if ( output ) {
2013-08-26 20:33:25 +02:00
if ( ! total . count_move & & ! total . count_restore & & ! total . count_change & & ! total . count_remove & & ! total . count_insert ) {
2012-02-03 19:16:14 +01:00
printf ( " No difference. \n " ) ;
2011-06-20 20:35:04 +02:00
}
}
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
}