diff options
Diffstat (limited to 'synchronization.cpp')
-rw-r--r-- | synchronization.cpp | 284 |
1 files changed, 177 insertions, 107 deletions
diff --git a/synchronization.cpp b/synchronization.cpp index 487bceb9..d54101fd 100644 --- a/synchronization.cpp +++ b/synchronization.cpp @@ -3,46 +3,18 @@ #include <wx/msgdlg.h> #include <wx/log.h> #include "library/multithreading.h" +#include "library/resources.h" +#include "algorithm.h" -#ifdef FFS_LINUX +#ifdef FFS_WIN +#include <wx/msw/wrapwin.h> //includes "windows.h" +#elif defined FFS_LINUX #include <utime.h> #endif // FFS_LINUX using namespace FreeFileSync; -SyncProcess::SyncProcess(bool useRecycler, bool lineBreakOnMessages, StatusHandler* handler) : - useRecycleBin(useRecycler), - statusUpdater(handler), - txtCopyingFile(_("Copying file %x to %y")), - txtOverwritingFile(_("Copying file %x overwriting %y")), - txtCreatingFolder(_("Creating folder %x")), - txtDeletingFile(_("Deleting file %x")), - txtDeletingFolder(_("Deleting folder %x")) -{ - if (lineBreakOnMessages) - { - txtCopyingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false); - txtCopyingFile.Replace( wxT("%y"), wxT("\n\"%y\""), false); - txtOverwritingFile.Replace(wxT("%x"), wxT("\n\"%x\""), false); - txtOverwritingFile.Replace(wxT("%y"), wxT("\n\"%y\""), false); - txtCreatingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false); - txtDeletingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false); - txtDeletingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false); - } - else - { - txtCopyingFile.Replace( wxT("%x"), wxT("\"%x\""), false); - txtCopyingFile.Replace( wxT("%y"), wxT("\"%y\""), false); - txtOverwritingFile.Replace(wxT("%x"), wxT("\"%x\""), false); - txtOverwritingFile.Replace(wxT("%y"), wxT("\"%y\""), false); - txtCreatingFolder.Replace( wxT("%x"), wxT("\"%x\""), false); - txtDeletingFile.Replace( wxT("%x"), wxT("\"%x\""), false); - txtDeletingFolder.Replace( wxT("%x"), wxT("\"%x\""), false); - } -} - - inline SyncConfiguration::Direction getSyncDirection(const CompareFilesResult cmpResult, const SyncConfiguration& config) { @@ -75,6 +47,7 @@ SyncConfiguration::Direction getSyncDirection(const CompareFilesResult cmpResult } +inline bool getBytesToTransfer(int& objectsToCreate, int& objectsToOverwrite, int& objectsToDelete, @@ -157,9 +130,97 @@ bool getBytesToTransfer(int& objectsToCreate, return true; } + +void FreeFileSync::calcTotalBytesToSync(const FileCompareResult& fileCmpResult, + const SyncConfiguration& config, + int& objectsToCreate, + int& objectsToOverwrite, + int& objectsToDelete, + double& dataToProcess) +{ + objectsToCreate = 0; + objectsToOverwrite = 0; + objectsToDelete = 0; + dataToProcess = 0; + + int toCreate = 0; + int toOverwrite = 0; + int toDelete = 0; + double data = 0; + + for (FileCompareResult::const_iterator i = fileCmpResult.begin(); i != fileCmpResult.end(); ++i) + { //only sum up sizes of files AND directories + if (getBytesToTransfer(toCreate, toOverwrite, toDelete, data, *i, config)) + { + objectsToCreate += toCreate; + objectsToOverwrite += toOverwrite; + objectsToDelete += toDelete; + dataToProcess += data; + } + } +} + + +bool FreeFileSync::synchronizationNeeded(const FileCompareResult& fileCmpResult, const SyncConfiguration& config) +{ + int objectsToCreate = 0; + int objectsToOverwrite = 0; + int objectsToDelete = 0; + double dataToProcess = 0; + FreeFileSync::calcTotalBytesToSync(fileCmpResult, + config, + objectsToCreate, + objectsToOverwrite, + objectsToDelete, + dataToProcess); + return objectsToCreate + objectsToOverwrite + objectsToDelete != 0; +} + + +//test if more then 50% of files will be deleted/overwritten +bool significantDifferenceDetected(const FileCompareResult& fileCmpResult, const SyncConfiguration& config) +{ + int objectsToCreate = 0; + int objectsToOverwrite = 0; + int objectsToDelete = 0; + double dataToProcess = 0; + FreeFileSync::calcTotalBytesToSync(fileCmpResult, + config, + objectsToCreate, + objectsToOverwrite, + objectsToDelete, + dataToProcess); + + const int changedFiles = objectsToOverwrite + objectsToDelete; + + return changedFiles >= 10 && changedFiles > .5 * fileCmpResult.size(); +} + + +SyncProcess::SyncProcess(const bool useRecycler, bool& warningSignificantDifference, StatusHandler* handler) : + useRecycleBin(useRecycler), + m_warningSignificantDifference(warningSignificantDifference), + statusUpdater(handler), + txtCopyingFile(_("Copying file %x to %y")), + txtOverwritingFile(_("Copying file %x overwriting %y")), + txtCreatingFolder(_("Creating folder %x")), + txtDeletingFile(_("Deleting file %x")), + txtDeletingFolder(_("Deleting folder %x")) +{ + txtCopyingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false); + txtCopyingFile.Replace( wxT("%y"), wxT("\n\"%y\""), false); + txtOverwritingFile.Replace(wxT("%x"), wxT("\n\"%x\""), false); + txtOverwritingFile.Replace(wxT("%y"), wxT("\n\"%y\""), false); + txtCreatingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false); + txtDeletingFile.Replace( wxT("%x"), wxT("\n\"%x\""), false); + txtDeletingFolder.Replace( wxT("%x"), wxT("\n\"%x\""), false); +} + + void copyfileMultithreaded(const Zstring& source, const Zstring& target, StatusHandler* updateClass); +inline bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConfiguration& config) { //return false if nothing had to be done @@ -186,7 +247,7 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf statusText = txtCopyingFile; statusText.Replace(wxT("%x"), cmpLine.fileDescrLeft.fullName, false); - statusText.Replace(wxT("%y"), cmpLine.fileDescrRight.directory, false); + statusText.Replace(wxT("%y"), target, false); statusUpdater->updateStatusText(statusText); copyfileMultithreaded(cmpLine.fileDescrLeft.fullName, target, statusUpdater); @@ -206,7 +267,7 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf statusText = txtCopyingFile; statusText.Replace(wxT("%x"), cmpLine.fileDescrRight.fullName, false); - statusText.Replace(wxT("%y"), cmpLine.fileDescrLeft.directory, false); + statusText.Replace(wxT("%y"), target, false); statusUpdater->updateStatusText(statusText); copyfileMultithreaded(cmpLine.fileDescrRight.fullName, target, statusUpdater); @@ -265,6 +326,7 @@ bool SyncProcess::synchronizeFile(const FileCompareLine& cmpLine, const SyncConf } +inline bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncConfiguration& config) { //false if nothing was to be done assert (statusUpdater); @@ -296,7 +358,7 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo //some check to catch the error that directory on source has been deleted externally after "compare"... if (!wxDirExists(cmpLine.fileDescrLeft.fullName)) - throw FileError(Zstring(_("Error: Source directory does not exist anymore:")) + wxT(" \"") + cmpLine.fileDescrLeft.fullName + wxT("\"")); + throw FileError(Zstring(_("Error: Source directory does not exist anymore:")) + wxT("\n\"") + cmpLine.fileDescrLeft.fullName + wxT("\"")); createDirectory(target); copyFolderAttributes(cmpLine.fileDescrLeft.fullName, target); break; @@ -319,7 +381,7 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo //some check to catch the error that directory on source has been deleted externally after "compare"... if (!wxDirExists(cmpLine.fileDescrRight.fullName)) - throw FileError(Zstring(_("Error: Source directory does not exist anymore:")) + wxT(" \"") + cmpLine.fileDescrRight.fullName + wxT("\"")); + throw FileError(Zstring(_("Error: Source directory does not exist anymore:")) + wxT("\n\"") + cmpLine.fileDescrRight.fullName + wxT("\"")); createDirectory(target); copyFolderAttributes(cmpLine.fileDescrRight.fullName, target); break; @@ -352,27 +414,26 @@ bool SyncProcess::synchronizeFolder(const FileCompareLine& cmpLine, const SyncCo inline bool deletionImminent(const FileCompareLine& line, const SyncConfiguration& config) { //test if current sync-line will result in deletion of files -> used to avoid disc space bottlenecks - if ( (line.cmpResult == FILE_LEFT_SIDE_ONLY && config.exLeftSideOnly == SyncConfiguration::SYNC_DIR_LEFT) || - (line.cmpResult == FILE_RIGHT_SIDE_ONLY && config.exRightSideOnly == SyncConfiguration::SYNC_DIR_RIGHT)) + if ( line.cmpResult == FILE_LEFT_SIDE_ONLY && config.exLeftSideOnly == SyncConfiguration::SYNC_DIR_LEFT || + line.cmpResult == FILE_RIGHT_SIDE_ONLY && config.exRightSideOnly == SyncConfiguration::SYNC_DIR_RIGHT) return true; else return false; } -class AlwaysWriteToGrid //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions are thrown! +class RemoveAtExit //this class ensures, that the result of the method below is ALWAYS written on exit, even if exceptions are thrown! { public: - AlwaysWriteToGrid(FileCompareResult& grid) : - gridToWrite(grid) - {} + RemoveAtExit(FileCompareResult& grid) : + gridToWrite(grid) {} - ~AlwaysWriteToGrid() + ~RemoveAtExit() { removeRowsFromVector(gridToWrite, rowsProcessed); } - void rowProcessedSuccessfully(int nr) + void removeRow(int nr) { rowsProcessed.insert(nr); } @@ -392,7 +453,28 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn wxLogNull noWxLogs; //prevent wxWidgets logging #endif - AlwaysWriteToGrid writeOutput(grid); //ensure that grid is always written to, even if method is exitted via exceptions + //some basic checks: + //test existence of Recycle Bin + if (useRecycleBin && !FreeFileSync::recycleBinExists()) + { + statusUpdater->reportFatalError(_("Unable to initialize Recycle Bin!")); + return; //should be obsolete! + } + + //check if more than 50% of files/dirs are to be overwritten/deleted + if (m_warningSignificantDifference) //test if check should be executed + { + if (significantDifferenceDetected(grid, config)) + { + bool dontShowAgain = false; + statusUpdater->reportWarning(_("Significant difference detected: More than 50% of files will be overwritten/deleted!"), + dontShowAgain); + m_warningSignificantDifference = !dontShowAgain; + } + } + + + RemoveAtExit markForRemoval(grid); //ensure that grid is always written to, even if method is exitted via exceptions //inform about the total amount of data that will be processed from now on int objectsToCreate = 0; @@ -414,7 +496,7 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn // and split into two "exists on one side only" cases // Note: this case is not handled by this tool as this is considered to be a bug and must be solved by the user - //synchronize folders: + //synchronize folders first; advantage: in case of deletions the whole folder is moved to recycle bin instead of single files for (FileCompareResult::const_iterator i = grid.begin(); i != grid.end(); ++i) { if ( i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY || @@ -429,15 +511,14 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn { if (synchronizeFolder(*i, config)) //progress indicator update - //indicator is updated only if directory is synched correctly (and if some sync was done)! + //indicator is updated only if directory is sync'ed correctly (and if some work was done)! statusUpdater->updateProcessedData(1, 0); //each call represents one processed file/directory - writeOutput.rowProcessedSuccessfully(i - grid.begin()); + markForRemoval.removeRow(i - grid.begin()); break; } catch (FileError& error) { - //if (updateClass) -> is mandatory ErrorHandler::Response rv = statusUpdater->reportError(error.show()); if ( rv == ErrorHandler::IGNORE_ERROR) @@ -451,6 +532,8 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn } } + //PERF_START; + //synchronize files: bool deleteLoop = true; for (int k = 0; k < 2; ++k) //loop over all files twice; reason: first delete, then copy (no sorting necessary: performance) @@ -489,12 +572,11 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn statusUpdater->updateProcessedData(objectsToCreate + objectsToOverwrite + objectsToDelete, dataToProcess); } - writeOutput.rowProcessedSuccessfully(i - grid.begin()); + markForRemoval.removeRow(i - grid.begin()); break; } catch (FileError& error) { - //if (updateClass) -> is mandatory ErrorHandler::Response rv = statusUpdater->reportError(error.show()); if ( rv == ErrorHandler::IGNORE_ERROR) @@ -512,9 +594,8 @@ void SyncProcess::startSynchronizationProcess(FileCompareResult& grid, const Syn } catch (const RuntimeException& theException) { - wxMessageBox(theException.show(), _("An exception occured!"), wxOK | wxICON_ERROR); - statusUpdater->requestAbortion(); - return; + statusUpdater->reportFatalError(theException.show().c_str()); + return; //should be obsolete! } } @@ -535,21 +616,56 @@ public: Zstring errorMessage; private: - void longRunner() //virtual impl. + virtual void longRunner() { + //create folders first (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080) + try + { + const Zstring targetDir = target.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); + if (!wxDirExists(targetDir.c_str())) + { + FreeFileSync::createDirectory(targetDir); + } + } + catch (FileError& e) + { + success = false; + errorMessage = e.show(); + return; + } + + +#ifdef FFS_WIN +//const DWORD COPY_FILE_COPY_SYMLINK = 0x00000800; + + if (!CopyFileEx( //same performance as CopyFile() + source.c_str(), + target.c_str(), + NULL, + NULL, + NULL, + COPY_FILE_FAIL_IF_EXISTS)) + { + success = false; + errorMessage = Zstring(_("Error copying file:")) + wxT("\n\"") + source + wxT("\" -> \"") + target + wxT("\""); + errorMessage += Zstring(wxT("\n\n")); + errorMessage += FreeFileSync::getLastErrorFormatted(); //some additional windows error information + return; + } + +#elif defined FFS_LINUX //copying files with Linux does not preserve the modification time => adapt after copying if (!wxCopyFile(source.c_str(), target.c_str(), false)) //abort if file exists { success = false; - errorMessage = Zstring(_("Error copying file:")) + wxT(" \"") + source + wxT("\" -> \"") + target + wxT("\""); + errorMessage = Zstring(_("Error copying file:")) + wxT("\n\"") + source + wxT("\" -> \"") + target + wxT("\""); return; } -#ifdef FFS_LINUX //copying files with Linux does not preserve the modification time => adapt after copying struct stat fileInfo; if (stat(source.c_str(), &fileInfo) != 0) //read modification time from source file { success = false; - errorMessage = Zstring(_("Could not retrieve file info for:")) + wxT(" \"") + source + wxT("\""); + errorMessage = Zstring(_("Could not retrieve file info for:")) + wxT("\n\"") + source + wxT("\""); return; } @@ -560,7 +676,7 @@ private: if (utime(target.c_str(), &newTimes) != 0) { success = false; - errorMessage = Zstring(_("Error changing modification time:")) + wxT(" \"") + target + wxT("\""); + errorMessage = Zstring(_("Error changing modification time:")) + wxT("\n\"") + target + wxT("\""); return; } #endif // FFS_LINUX @@ -572,7 +688,7 @@ private: void copyfileMultithreaded(const Zstring& source, const Zstring& target, StatusHandler* updateClass) { - static UpdateWhileCopying copyAndUpdate; //single instantiation: after each execution thread enters wait phase + static UpdateWhileCopying copyAndUpdate; //single instantiation: thread enters wait phase after each execution copyAndUpdate.waitUntilReady(); @@ -586,49 +702,3 @@ void copyfileMultithreaded(const Zstring& source, const Zstring& target, StatusH if (!copyAndUpdate.success) throw FileError(copyAndUpdate.errorMessage); } - - -void FreeFileSync::calcTotalBytesToSync(const FileCompareResult& fileCmpResult, - const SyncConfiguration& config, - int& objectsToCreate, - int& objectsToOverwrite, - int& objectsToDelete, - double& dataToProcess) -{ - objectsToCreate = 0; - objectsToOverwrite = 0; - objectsToDelete = 0; - dataToProcess = 0; - - int toCreate = 0; - int toOverwrite = 0; - int toDelete = 0; - double data = 0; - - for (FileCompareResult::const_iterator i = fileCmpResult.begin(); i != fileCmpResult.end(); ++i) - { //only sum up sizes of files AND directories - if (getBytesToTransfer(toCreate, toOverwrite, toDelete, data, *i, config)) - { - objectsToCreate += toCreate; - objectsToOverwrite += toOverwrite; - objectsToDelete += toDelete; - dataToProcess += data; - } - } -} - - -bool FreeFileSync::synchronizationNeeded(const FileCompareResult& fileCmpResult, const SyncConfiguration& config) -{ - int objectsToCreate = 0; - int objectsToOverwrite = 0; - int objectsToDelete = 0; - double dataToProcess = 0; - FreeFileSync::calcTotalBytesToSync(fileCmpResult, - config, - objectsToCreate, - objectsToOverwrite, - objectsToDelete, - dataToProcess); - return objectsToCreate + objectsToOverwrite + objectsToDelete != 0; -} |