2016-09-12 18:01:26 -04:00
/*
* RetroShare Hash cache
*
* file_sharing / hash_cache . cc
*
* Copyright 2016 Mr . Alice
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License Version 2 as published by the Free Software Foundation .
*
* This library 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
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA .
*
* Please report all bugs and problems to " retroshare.project@gmail.com " .
*
*/
2016-07-27 15:22:59 -04:00
# include "util/rsdir.h"
2016-09-05 15:22:11 -04:00
# include "util/rsprint.h"
2016-08-06 13:04:54 -04:00
# include "rsserver/p3face.h"
2016-08-09 09:07:02 -04:00
# include "pqi/authssl.h"
2016-07-27 15:22:59 -04:00
# include "hash_cache.h"
2016-08-09 09:07:02 -04:00
# include "filelist_io.h"
2016-09-02 15:49:43 -04:00
# include "file_sharing_defaults.h"
2016-07-27 15:22:59 -04:00
2016-09-18 16:05:27 -04:00
//#define HASHSTORAGE_DEBUG 1
2016-07-27 15:22:59 -04:00
2016-08-09 09:07:02 -04:00
static const uint32_t DEFAULT_INACTIVITY_SLEEP_TIME = 50 * 1000 ;
static const uint32_t MAX_INACTIVITY_SLEEP_TIME = 2 * 1000 * 1000 ;
2016-07-27 15:22:59 -04:00
HashStorage : : HashStorage ( const std : : string & save_file_name )
: mFilePath ( save_file_name ) , mHashMtx ( " Hash Storage mutex " )
{
2016-08-16 07:46:55 -04:00
mInactivitySleepTime = DEFAULT_INACTIVITY_SLEEP_TIME ;
2016-07-27 18:48:28 -04:00
mRunning = false ;
2016-09-02 15:49:43 -04:00
mLastSaveTime = 0 ;
2016-09-15 15:40:53 -04:00
mTotalSizeToHash = 0 ;
mTotalFilesToHash = 0 ;
2016-09-18 12:34:39 -04:00
mMaxStorageDurationDays = DEFAULT_HASH_STORAGE_DURATION_DAYS ;
2016-08-16 07:46:55 -04:00
2016-09-02 15:49:43 -04:00
{
RS_STACK_MUTEX ( mHashMtx ) ;
2016-09-26 15:12:13 -04:00
if ( ! locked_load ( ) )
try_load_import_old_hash_cache ( ) ;
2016-09-02 15:49:43 -04:00
}
2016-07-27 15:22:59 -04:00
}
2016-09-15 15:40:53 -04:00
static std : : string friendlyUnit ( uint64_t val )
{
const std : : string units [ 5 ] = { " B " , " KB " , " MB " , " GB " , " TB " } ;
char buf [ 50 ] ;
double fact = 1.0 ;
for ( unsigned int i = 0 ; i < 5 ; + + i )
if ( double ( val ) / fact < 1024.0 )
{
sprintf ( buf , " %2.2f " , double ( val ) / fact ) ;
return std : : string ( buf ) + " " + units [ i ] ;
}
else
fact * = 1024.0f ;
sprintf ( buf , " %2.2f " , double ( val ) / fact * 1024.0f ) ;
return std : : string ( buf ) + " TB " ;
}
2016-07-27 15:22:59 -04:00
void HashStorage : : data_tick ( )
{
FileHashJob job ;
2016-07-27 18:48:28 -04:00
RsFileHash hash ;
2016-10-01 12:01:09 -04:00
uint64_t size = 0 ;
2016-07-27 15:22:59 -04:00
{
2016-08-12 13:30:19 -04:00
bool empty ;
uint32_t st ;
2016-07-27 15:22:59 -04:00
2016-09-02 16:08:27 -04:00
{
RS_STACK_MUTEX ( mHashMtx ) ;
if ( mChanged & & mLastSaveTime + MIN_INTERVAL_BETWEEN_HASH_CACHE_SAVE < time ( NULL ) )
{
locked_save ( ) ;
mLastSaveTime = time ( NULL ) ;
mChanged = false ;
}
}
2016-08-05 16:37:40 -04:00
{
2016-08-12 13:30:19 -04:00
RS_STACK_MUTEX ( mHashMtx ) ;
2016-08-05 16:37:40 -04:00
2016-08-12 13:30:19 -04:00
empty = mFilesToHash . empty ( ) ;
st = mInactivitySleepTime ;
}
// sleep off mutex!
if ( empty )
{
2016-09-18 16:05:27 -04:00
# ifdef HASHSTORAGE_DEBUG
2016-08-12 13:30:19 -04:00
std : : cerr < < " nothing to hash. Sleeping for " < < st < < " us " < < std : : endl ;
2016-09-18 16:05:27 -04:00
# endif
2016-08-12 13:30:19 -04:00
usleep ( st ) ; // when no files to hash, just wait for 2 secs. This avoids a dramatic loop.
2016-08-06 13:04:54 -04:00
2016-08-12 13:30:19 -04:00
if ( st > MAX_INACTIVITY_SLEEP_TIME )
{
RS_STACK_MUTEX ( mHashMtx ) ;
2016-08-09 09:07:02 -04:00
2016-08-12 13:30:19 -04:00
mInactivitySleepTime = MAX_INACTIVITY_SLEEP_TIME ;
2016-09-02 15:49:43 -04:00
if ( ! mChanged ) // otherwise it might prevent from saving the hash cache
{
std : : cerr < < " Stopping hashing thread. " < < std : : endl ;
shutdown ( ) ;
mRunning = false ;
2016-09-15 15:40:53 -04:00
mTotalSizeToHash = 0 ;
mTotalFilesToHash = 0 ;
2016-09-02 15:49:43 -04:00
std : : cerr < < " done. " < < std : : endl ;
}
2016-08-12 13:30:19 -04:00
RsServer : : notify ( ) - > notifyHashingInfo ( NOTIFY_HASHTYPE_FINISH , " " ) ;
}
else
{
RS_STACK_MUTEX ( mHashMtx ) ;
mInactivitySleepTime = 2 * st ;
}
2016-08-09 09:07:02 -04:00
2016-07-27 15:22:59 -04:00
return ;
2016-08-05 16:37:40 -04:00
}
2016-08-09 09:07:02 -04:00
mInactivitySleepTime = DEFAULT_INACTIVITY_SLEEP_TIME ;
2016-07-27 15:22:59 -04:00
2016-08-16 07:46:55 -04:00
{
RS_STACK_MUTEX ( mHashMtx ) ;
2016-09-15 15:40:53 -04:00
2016-08-16 07:46:55 -04:00
job = mFilesToHash . begin ( ) - > second ;
mFilesToHash . erase ( mFilesToHash . begin ( ) ) ;
}
2016-07-27 15:22:59 -04:00
2016-09-18 12:34:39 -04:00
if ( job . client - > hash_confirm ( job . client_param ) )
{
2016-11-16 15:41:32 -05:00
# ifdef HASHSTORAGE_DEBUG
2016-09-18 12:34:39 -04:00
std : : cerr < < " Hashing file " < < job . full_path < < " ... " ; std : : cerr . flush ( ) ;
2016-11-16 15:41:32 -05:00
# endif
2016-07-27 15:22:59 -04:00
2016-09-18 12:34:39 -04:00
std : : string tmpout ;
2016-09-23 15:56:41 -04:00
rs_sprintf ( tmpout , " %lu/%lu (%s - %d%%) : %s " , ( unsigned long int ) mHashCounter + 1 , ( unsigned long int ) mTotalFilesToHash , friendlyUnit ( mTotalHashedSize ) . c_str ( ) , int ( mTotalHashedSize / double ( mTotalSizeToHash ) * 100.0 ) , job . full_path . c_str ( ) ) ;
2016-08-06 13:04:54 -04:00
2016-09-18 12:34:39 -04:00
RsServer : : notify ( ) - > notifyHashingInfo ( NOTIFY_HASHTYPE_HASH_FILE , tmpout ) ;
2016-07-27 15:22:59 -04:00
2016-09-25 12:44:28 -04:00
if ( RsDirUtil : : getFileHash ( job . full_path , hash , size , this ) )
{
// store the result
2016-07-27 15:22:59 -04:00
2016-11-16 15:41:32 -05:00
# ifdef HASHSTORAGE_DEBUG
2016-09-25 12:44:28 -04:00
std : : cerr < < " done. " < < std : : endl ;
2016-11-16 15:41:32 -05:00
# endif
2016-07-28 04:49:49 -04:00
2016-09-18 12:34:39 -04:00
RS_STACK_MUTEX ( mHashMtx ) ;
HashStorageInfo & info ( mFiles [ job . full_path ] ) ;
2016-07-28 04:49:49 -04:00
2016-09-18 12:34:39 -04:00
info . filename = job . full_path ;
info . size = size ;
info . modf_stamp = job . ts ;
info . time_stamp = time ( NULL ) ;
info . hash = hash ;
2016-09-02 15:49:43 -04:00
2016-09-18 12:34:39 -04:00
mChanged = true ;
mTotalHashedSize + = size ;
}
2016-09-25 12:44:28 -04:00
else
std : : cerr < < " ERROR: cannot hash file " < < job . full_path < < std : : endl ;
+ + mHashCounter ;
2016-09-02 16:08:27 -04:00
}
2016-07-27 18:48:28 -04:00
}
2016-07-27 15:22:59 -04:00
// call the client
2016-07-27 18:48:28 -04:00
if ( ! hash . isNull ( ) )
job . client - > hash_callback ( job . client_param , job . full_path , hash , size ) ;
2016-07-27 15:22:59 -04:00
}
bool HashStorage : : requestHash ( const std : : string & full_path , uint64_t size , time_t mod_time , RsFileHash & known_hash , HashStorageClient * c , uint32_t client_param )
{
// check if the hash is up to date w.r.t. cache.
2016-07-28 04:49:49 -04:00
# ifdef HASHSTORAGE_DEBUG
std : : cerr < < " HASH Requested for file " < < full_path < < " : " ;
# endif
2016-07-27 15:22:59 -04:00
RS_STACK_MUTEX ( mHashMtx ) ;
time_t now = time ( NULL ) ;
std : : map < std : : string , HashStorageInfo > : : iterator it = mFiles . find ( full_path ) ;
2016-09-23 14:07:57 -04:00
// On windows we compare the time up to +/- 3600 seconds. This avoids re-hashing files in case of daylight saving change.
//
// See:
// https://support.microsoft.com/en-us/kb/190315
//
if ( it ! = mFiles . end ( )
# ifdef WINDOWS_SYS
& & ( ( uint64_t ) mod_time = = it - > second . modf_stamp | | ( uint64_t ) mod_time + 3600 = = it - > second . modf_stamp | | ( uint64_t ) mod_time = = it - > second . modf_stamp + 3600 )
# else
& & ( uint64_t ) mod_time = = it - > second . modf_stamp
# endif
& & size = = it - > second . size )
2016-07-27 15:22:59 -04:00
{
it - > second . time_stamp = now ;
2016-09-23 14:07:57 -04:00
# ifdef WINDOWS_SYS
if ( it - > second . time_stamp ! = ( uint64_t ) mod_time )
{
std : : cerr < < " (WW) detected a 1 hour shift in file modification time. This normally happens to many files at once, when daylight saving time shifts (file= \" " < < full_path < < " \" ). " < < std : : endl ;
it - > second . time_stamp = ( uint64_t ) mod_time ;
}
# endif
2016-07-28 04:49:49 -04:00
known_hash = it - > second . hash ;
# ifdef HASHSTORAGE_DEBUG
2016-07-27 15:22:59 -04:00
std : : cerr < < " Found in cache. " < < std : : endl ;
# endif
return true ;
}
2016-07-28 04:49:49 -04:00
# ifdef HASHSTORAGE_DEBUG
2016-08-29 15:30:56 -04:00
std : : cerr < < " Not in cache. Scheduling for re-hash. " < < std : : endl ;
2016-07-28 04:49:49 -04:00
# endif
2016-07-27 15:22:59 -04:00
// we need to schedule a re-hashing
if ( mFilesToHash . find ( full_path ) ! = mFilesToHash . end ( ) )
return false ;
FileHashJob job ;
job . client = c ;
2016-09-15 15:40:53 -04:00
job . size = size ;
2016-07-27 15:22:59 -04:00
job . client_param = client_param ;
job . full_path = full_path ;
2016-07-28 04:49:49 -04:00
job . ts = mod_time ;
2016-07-27 15:22:59 -04:00
mFilesToHash [ full_path ] = job ;
2016-09-15 15:40:53 -04:00
mTotalSizeToHash + = size ;
+ + mTotalFilesToHash ;
2016-07-27 18:48:28 -04:00
if ( ! mRunning )
2016-07-27 15:22:59 -04:00
{
2016-07-27 18:48:28 -04:00
mRunning = true ;
2016-07-27 15:22:59 -04:00
std : : cerr < < " Starting hashing thread. " < < std : : endl ;
2016-09-15 15:40:53 -04:00
mHashCounter = 0 ;
mTotalHashedSize = 0 ;
2016-09-27 03:50:59 -04:00
start ( " fs hash cache " ) ;
2016-07-27 15:22:59 -04:00
}
return false ;
}
void HashStorage : : clean ( )
{
2016-09-02 16:08:27 -04:00
RS_STACK_MUTEX ( mHashMtx ) ;
2016-07-27 15:22:59 -04:00
time_t now = time ( NULL ) ;
time_t duration = mMaxStorageDurationDays * 24 * 3600 ; // seconds
# ifdef HASHSTORAGE_DEBUG
2016-09-18 12:34:39 -04:00
std : : cerr < < " Cleaning hash cache. " < < std : : endl ;
2016-07-27 15:22:59 -04:00
# endif
for ( std : : map < std : : string , HashStorageInfo > : : iterator it ( mFiles . begin ( ) ) ; it ! = mFiles . end ( ) ; )
2016-10-12 14:43:38 -04:00
if ( ( uint64_t ) ( it - > second . time_stamp + duration ) < ( uint64_t ) now )
2016-07-27 15:22:59 -04:00
{
# ifdef HASHSTORAGE_DEBUG
std : : cerr < < " Entry too old: " < < it - > first < < " , ts= " < < it - > second . time_stamp < < std : : endl ;
# endif
std : : map < std : : string , HashStorageInfo > : : iterator tmp ( it ) ;
+ + tmp ;
mFiles . erase ( it ) ;
it = tmp ;
mChanged = true ;
}
else
+ + it ;
# ifdef HASHSTORAGE_DEBUG
std : : cerr < < " Done. " < < std : : endl ;
# endif
}
2016-09-26 15:12:13 -04:00
bool HashStorage : : locked_load ( )
2016-08-09 09:07:02 -04:00
{
2016-09-03 12:46:03 -04:00
unsigned char * data = NULL ;
uint32_t data_size = 0 ;
2016-08-09 09:07:02 -04:00
2016-09-03 12:46:03 -04:00
if ( ! FileListIO : : loadEncryptedDataFromFile ( mFilePath , data , data_size ) )
2016-08-09 09:07:02 -04:00
{
2016-09-03 12:46:03 -04:00
std : : cerr < < " (EE) Cannot read hash cache. " < < std : : endl ;
2016-09-26 15:12:13 -04:00
return false ;
2016-08-09 09:07:02 -04:00
}
uint32_t offset = 0 ;
HashStorageInfo info ;
2016-09-27 03:50:59 -04:00
# ifdef HASHSTORAGE_DEBUG
2016-09-03 12:46:03 -04:00
uint32_t n = 0 ;
2016-09-27 03:50:59 -04:00
# endif
2016-08-09 09:07:02 -04:00
2016-09-03 12:46:03 -04:00
while ( offset < data_size )
if ( readHashStorageInfo ( data , data_size , offset , info ) )
2016-08-09 09:07:02 -04:00
{
# ifdef HASHSTORAGE_DEBUG
std : : cerr < < info < < std : : endl ;
+ + n ;
# endif
mFiles [ info . filename ] = info ;
}
2016-09-03 12:46:03 -04:00
free ( data ) ;
2016-08-09 09:07:02 -04:00
# ifdef HASHSTORAGE_DEBUG
std : : cerr < < n < < " entries loaded. " < < std : : endl ;
# endif
2016-09-26 15:12:13 -04:00
return true ;
2016-08-09 09:07:02 -04:00
}
2016-09-02 15:49:43 -04:00
void HashStorage : : locked_save ( )
2016-08-09 09:07:02 -04:00
{
# ifdef HASHSTORAGE_DEBUG
std : : cerr < < " Saving Hash Cache to file " < < mFilePath < < " ... " < < std : : endl ;
# endif
unsigned char * data = NULL ;
uint32_t offset = 0 ;
uint32_t total_size = 0 ;
for ( std : : map < std : : string , HashStorageInfo > : : const_iterator it ( mFiles . begin ( ) ) ; it ! = mFiles . end ( ) ; + + it )
2016-08-12 09:20:23 -04:00
writeHashStorageInfo ( data , total_size , offset , it - > second ) ;
2016-08-09 09:07:02 -04:00
2016-09-03 12:46:03 -04:00
if ( ! FileListIO : : saveEncryptedDataToFile ( mFilePath , data , offset ) )
2016-08-09 09:07:02 -04:00
{
2016-09-03 12:46:03 -04:00
std : : cerr < < " (EE) Cannot save hash cache data. " < < std : : endl ;
free ( data ) ;
return ;
2016-08-09 09:07:02 -04:00
}
2016-09-18 15:16:25 -04:00
std : : cerr < < mFiles . size ( ) < < " entries saved in hash cache. " < < std : : endl ;
2016-08-09 09:07:02 -04:00
2016-09-03 12:46:03 -04:00
free ( data ) ;
2016-08-09 09:07:02 -04:00
}
bool HashStorage : : readHashStorageInfo ( const unsigned char * data , uint32_t total_size , uint32_t & offset , HashStorageInfo & info ) const
{
unsigned char * section_data = NULL ;
uint32_t section_size = 0 ;
2016-08-12 09:20:23 -04:00
uint32_t section_offset = 0 ;
2016-08-09 09:07:02 -04:00
2016-08-12 13:30:19 -04:00
// This way, the entire section is either read or skipped. That avoids the risk of being stuck somewhere in the middle
// of a section because of some unknown field, etc.
2016-08-09 09:07:02 -04:00
if ( ! FileListIO : : readField ( data , total_size , offset , FILE_LIST_IO_TAG_HASH_STORAGE_ENTRY , section_data , section_size ) )
return false ;
if ( ! FileListIO : : readField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_FILE_NAME , info . filename ) ) return false ;
if ( ! FileListIO : : readField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_FILE_SIZE , info . size ) ) return false ;
if ( ! FileListIO : : readField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_UPDATE_TS , info . time_stamp ) ) return false ;
if ( ! FileListIO : : readField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_MODIF_TS , info . modf_stamp ) ) return false ;
if ( ! FileListIO : : readField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_FILE_SHA1_HASH , info . hash ) ) return false ;
free ( section_data ) ;
return true ;
}
bool HashStorage : : writeHashStorageInfo ( unsigned char * & data , uint32_t & total_size , uint32_t & offset , const HashStorageInfo & info ) const
{
unsigned char * section_data = NULL ;
uint32_t section_offset = 0 ;
uint32_t section_size = 0 ;
if ( ! FileListIO : : writeField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_FILE_NAME , info . filename ) ) return false ;
if ( ! FileListIO : : writeField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_FILE_SIZE , info . size ) ) return false ;
if ( ! FileListIO : : writeField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_UPDATE_TS , info . time_stamp ) ) return false ;
if ( ! FileListIO : : writeField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_MODIF_TS , info . modf_stamp ) ) return false ;
if ( ! FileListIO : : writeField ( section_data , section_size , section_offset , FILE_LIST_IO_TAG_FILE_SHA1_HASH , info . hash ) ) return false ;
// now write the whole string into a single section in the file
if ( ! FileListIO : : writeField ( data , total_size , offset , FILE_LIST_IO_TAG_HASH_STORAGE_ENTRY , section_data , section_offset ) ) return false ;
2016-09-18 15:16:25 -04:00
# ifdef HASHSTORAGE_DEBUG
2016-09-05 15:22:11 -04:00
std : : cerr < < " Writing hash storage section " < < RsUtil : : BinToHex ( section_data , section_offset ) < < std : : endl ;
std : : cerr < < " Info.filename = " < < info . filename < < std : : endl ;
2016-09-18 15:16:25 -04:00
# endif
2016-08-09 09:07:02 -04:00
free ( section_data ) ;
return true ;
}
std : : ostream & operator < < ( std : : ostream & o , const HashStorage : : HashStorageInfo & info )
{
return o < < info . hash < < " " < < info . size < < " " < < info . filename ;
}
2016-09-26 15:12:13 -04:00
/********************************************************************************************************************************/
/* 09/26/2016 */
/* This method should be removed in a few months. It only helps importing the old hash cache in order to */
/* spare the re-hashing of everything. If successful, it also renames the old hash cache into a new name so that */
/* the file is not found at the next attempt. */
/********************************************************************************************************************************/
//
# include "rsserver/rsaccounts.h"
# include <sstream>
bool HashStorage : : try_load_import_old_hash_cache ( )
{
// compute file name
std : : string base_dir = rsAccounts - > PathAccountDirectory ( ) ;
std : : string old_cache_filename = base_dir + " / " + " file_cache.bin " ;
// check for unencrypted
std : : istream * f = NULL ;
uint64_t file_size ;
if ( RsDirUtil : : checkFile ( old_cache_filename , file_size , false ) )
{
std : : cerr < < " Encrypted hash cache file present. Reading it. " < < std : : endl ;
// read the binary stream into memory.
//
RsTemporaryMemory buffer ( file_size ) ;
if ( buffer = = NULL )
return false ;
FILE * F = fopen ( old_cache_filename . c_str ( ) , " rb " ) ;
if ( ! F )
{
std : : cerr < < " Cannot open file for reading encrypted file cache, filename " < < old_cache_filename < < std : : endl ;
return false ;
}
if ( fread ( buffer , 1 , file_size , F ) ! = file_size )
{
std : : cerr < < " Cannot read from file " < < old_cache_filename < < " : something's wrong. " < < std : : endl ;
fclose ( F ) ;
return false ;
}
fclose ( F ) ;
void * decrypted_data = NULL ;
int decrypted_data_size = 0 ;
if ( ! AuthSSL : : getAuthSSL ( ) - > decrypt ( decrypted_data , decrypted_data_size , buffer , file_size ) )
{
std : : cerr < < " Cannot decrypt encrypted file cache. Something's wrong. " < < std : : endl ;
return false ;
}
std : : string s ( ( char * ) decrypted_data , decrypted_data_size ) ;
f = new std : : istringstream ( s ) ;
free ( decrypted_data ) ;
}
else
return false ;
std : : streamsize max_line_size = 2000 ; // should be enough. Anyway, if we
// miss one entry, we just lose some
// cache itemsn but this is not too
// much of a problem.
char * buff = new char [ max_line_size ] ;
std : : cerr < < " Importing hashCache from file " < < old_cache_filename < < std : : endl ;
int n = 0 ;
std : : map < std : : string , HashStorageInfo > tmp_files ; // stored as (full_path, hash_info)
while ( ! f - > eof ( ) )
{
HashStorageInfo info ;
f - > getline ( buff , max_line_size , ' \n ' ) ;
info . filename = std : : string ( buff ) ;
f - > getline ( buff , max_line_size , ' \n ' ) ; //if(sscanf(buff,"%llu",&info.size) != 1) break ;
info . size = 0 ;
sscanf ( buff , RsDirUtil : : scanf_string_for_uint ( sizeof ( info . size ) ) , & info . size ) ;
f - > getline ( buff , max_line_size , ' \n ' ) ; if ( sscanf ( buff , RsDirUtil : : scanf_string_for_uint ( sizeof ( info . time_stamp ) ) , & info . time_stamp ) ! = 1 ) { std : : cerr < < " Could not read one entry! Giving up. " < < std : : endl ; break ; }
f - > getline ( buff , max_line_size , ' \n ' ) ; if ( sscanf ( buff , RsDirUtil : : scanf_string_for_uint ( sizeof ( info . modf_stamp ) ) , & info . modf_stamp ) ! = 1 ) { std : : cerr < < " Could not read one entry! Giving up. " < < std : : endl ; break ; }
f - > getline ( buff , max_line_size , ' \n ' ) ; info . hash = RsFileHash ( std : : string ( buff ) ) ;
std : : cerr < < " ( " < < info . filename < < " , " < < info . size < < " , " < < info . time_stamp < < " , " < < info . modf_stamp < < " , " < < info . hash < < std : : endl ;
+ + n ;
tmp_files [ info . filename ] = info ;
}
delete [ ] buff ;
delete f ;
std : : cerr < < " (II) Successfully imported hash cache from file " < < old_cache_filename < < " for a total of " < < n < < " entries. " < < std : : endl ;
std : : cerr < < " (II) This file is now obsolete, and will be renamed " < < old_cache_filename + " .bak " < < " in case you needed it. But you can safely remove it. " < < std : : endl ;
RsDirUtil : : renameFile ( old_cache_filename , old_cache_filename + " .bak " ) ;
mFiles = tmp_files ;
locked_save ( ) ; // this is called explicitly here because the ticking thread is not active.
return true ;
}
/********************************************************************************************************************************/