diff options
Diffstat (limited to 'synchronization.cpp')
-rw-r--r-- | synchronization.cpp | 570 |
1 files changed, 306 insertions, 264 deletions
diff --git a/synchronization.cpp b/synchronization.cpp index 66108ade..896de7ce 100644 --- a/synchronization.cpp +++ b/synchronization.cpp @@ -12,6 +12,11 @@ #include <boost/bind.hpp> #include "shared/globalFunctions.h" #include <boost/scoped_array.hpp> +#include <memory> + +#ifdef FFS_WIN +#include "shared/shadow.h" +#endif using namespace FreeFileSync; @@ -322,6 +327,9 @@ bool significantDifferenceDetected(const SyncStatistics& folderPairStat) } +//################################################################################################################# + + /*add some postfix to alternate deletion directory: customDir\2009-06-30 12-59-12\ */ Zstring getSessionDeletionDir(const Zstring& customDeletionDirectory) { @@ -359,72 +367,231 @@ SyncProcess::SyncProcess(const bool copyFileSymLinks, const bool traverseDirSymLinks, xmlAccess::OptionalDialogs& warnings, const bool verifyCopiedFiles, - StatusHandler* handler) : + StatusHandler& handler) : m_copyFileSymLinks(copyFileSymLinks), m_traverseDirSymLinks(traverseDirSymLinks), m_verifyCopiedFiles(verifyCopiedFiles), m_warnings(warnings), + statusUpdater(handler) {} + + +struct DeletionHandling +{ + DeletionHandling(const DeletionPolicy handleDel, + const Zstring& custDelFolder) : + handleDeletion(handleDel), + currentDelFolder(getSessionDeletionDir(custDelFolder)), //ends with path separator + txtMoveFileUserDefined( wxToZ(_("Moving file %x to user-defined directory %y")). Replace(DefaultStr("%x"), DefaultStr("\"%x\"\n"), false).Replace(DefaultStr("%y"), Zstring(DefaultStr("\"")) + custDelFolder + DefaultStr("\""), false)), + txtMoveFolderUserDefined(wxToZ(_("Moving folder %x to user-defined directory %y")).Replace(DefaultStr("%x"), DefaultStr("\"%x\"\n"), false).Replace(DefaultStr("%y"), Zstring(DefaultStr("\"")) + custDelFolder + DefaultStr("\""), false)) + {} + + DeletionPolicy handleDeletion; + Zstring currentDelFolder; //alternate deletion folder for current folder pair (with timestamp, ends with path separator) + //preloaded status texts: + const Zstring txtMoveFileUserDefined; + const Zstring txtMoveFolderUserDefined; +}; + + +template <typename Function> +inline +void tryReportingError(StatusHandler& handler, Function cmd) +{ + while (true) + { + try + { + cmd(); + break; + } + catch (FileError& error) + { + //User abort when copying files or moving files/directories into custom deletion directory: + //windows build: abort if requested, don't show error message if cancelled by user! + //linux build: this refresh is not necessary, because user abort triggers an AbortThisProcess() exception without a FileError() + handler.requestUiRefresh(true); //may throw! + + ErrorHandler::Response rv = handler.reportError(error.show()); //may throw! + if ( rv == ErrorHandler::IGNORE_ERROR) + break; + else if (rv == ErrorHandler::RETRY) + ; //continue with loop + else + throw std::logic_error("Programming Error: Unknown return value!"); + } + } +} + + +inline +bool deletionImminent(const FileSystemObject& fsObj) +{ + //test if current sync-line will result in deletion of files -> used to avoid disc space bottlenecks + const SyncOperation op = fsObj.getSyncOperation(); + return op == FreeFileSync::SO_DELETE_LEFT || op == FreeFileSync::SO_DELETE_RIGHT; +} + + +class RemoveInvalid +{ +public: + RemoveInvalid(HierarchyObject& hierObj) : + hierObj_(hierObj) {} + + ~RemoveInvalid() + { + FileSystemObject::removeEmptyNonRec(hierObj_); + } + +private: + HierarchyObject& hierObj_; +}; + + +class FreeFileSync::SyncRecursively +{ +public: + SyncRecursively(const SyncProcess& syncProc, #ifdef FFS_WIN - shadowCopyHandler(new ShadowCopy), + ShadowCopy& shadowCopyHandler, #endif - statusUpdater(handler), - txtCopyingFile(wxToZ(_("Copying file %x to %y")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false).Replace(DefaultStr("%y"), DefaultStr("\n\"%y\""), false)), - txtOverwritingFile(wxToZ(_("Copying file %x to %y overwriting target")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false).Replace(DefaultStr("%y"), DefaultStr("\n\"%y\""), false)), - txtCreatingFolder(wxToZ(_("Creating folder %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)), - txtDeletingFile(wxToZ(_("Deleting file %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)), - txtDeletingFolder(wxToZ(_("Deleting folder %x")).Replace( DefaultStr("%x"), DefaultStr("\n\"%x\""), false)), - txtMoveToRecycler(wxToZ(_("Moving %x to Recycle Bin")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false)), - txtVerifying(wxToZ(_("Verifying file %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)) -{} + const DeletionHandling& delHandling) : + statusUpdater_(syncProc.statusUpdater), +#ifdef FFS_WIN + shadowCopyHandler_(shadowCopyHandler), +#endif + delHandling_(delHandling), + copyFileSymLinks_(syncProc.m_copyFileSymLinks), + traverseDirSymLinks_(syncProc.m_traverseDirSymLinks), + verifyCopiedFiles_(syncProc.m_verifyCopiedFiles), + txtCopyingFile(wxToZ(_("Copying file %x to %y")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false).Replace(DefaultStr("%y"), DefaultStr("\n\"%y\""), false)), + txtOverwritingFile(wxToZ(_("Copying file %x to %y overwriting target")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false).Replace(DefaultStr("%y"), DefaultStr("\n\"%y\""), false)), + txtCreatingFolder(wxToZ(_("Creating folder %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)), + txtDeletingFile(wxToZ(_("Deleting file %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)), + txtDeletingFolder(wxToZ(_("Deleting folder %x")).Replace( DefaultStr("%x"), DefaultStr("\n\"%x\""), false)), + txtMoveToRecycler(wxToZ(_("Moving %x to Recycle Bin")).Replace(DefaultStr("%x"), DefaultStr("\"%x\""), false)), + txtVerifying(wxToZ(_("Verifying file %x")).Replace(DefaultStr("%x"), DefaultStr("\n\"%x\""), false)) {} + + template <bool deleteOnly> //"true" if files deletion shall happen only + void execute(HierarchyObject& hierObj) + { + //enforce removal of invalid entries (where both sides are empty) + RemoveInvalid dummy(hierObj); //non-recursive + + //synchronize files: + for (HierarchyObject::SubFileMapping::iterator i = hierObj.subFiles.begin(); i != hierObj.subFiles.end(); ++i) + { + if ( ( deleteOnly && deletionImminent(*i)) || + (!deleteOnly && !deletionImminent(*i))) + tryReportingError(statusUpdater_, boost::bind(&SyncRecursively::synchronizeFile, this, boost::ref(*i))); + } + + //synchronize folders: + for (HierarchyObject::SubDirMapping::iterator i = hierObj.subDirs.begin(); i != hierObj.subDirs.end(); ++i) + { + const SyncOperation syncOp = i->getSyncOperation(); + + if ( ( deleteOnly && deletionImminent(*i)) || //ensure folder creation happens in second pass, to enable time adaption below + (!deleteOnly && !deletionImminent(*i))) // + tryReportingError(statusUpdater_, boost::bind(&SyncRecursively::synchronizeFolder, this, boost::ref(*i))); + + //recursive synchronization: + execute<deleteOnly>(*i); + + //adapt folder modification dates: apply AFTER all subobjects have been synced to preserve folder modification date! + switch (syncOp) + { + case SO_CREATE_NEW_LEFT: + copyFileTimes(i->getFullName<RIGHT_SIDE>(), i->getFullName<LEFT_SIDE>()); //throw() + break; + case SO_CREATE_NEW_RIGHT: + copyFileTimes(i->getFullName<LEFT_SIDE>(), i->getFullName<RIGHT_SIDE>()); //throw() + break; + case SO_OVERWRITE_RIGHT: + case SO_OVERWRITE_LEFT: + case SO_UNRESOLVED_CONFLICT: + assert(false); + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + case SO_DO_NOTHING: + ; + } + } + } + +private: + void synchronizeFile(FileMapping& fileObj) const; + void synchronizeFolder(DirMapping& dirObj) const; + + template <FreeFileSync::SelectedSide side> + void removeFile(const FileMapping& fileObj, bool showStatusUpdate) const; + template <FreeFileSync::SelectedSide side> + void removeFolder(const DirMapping& dirObj) const; -SyncProcess::DeletionHandling::DeletionHandling(const DeletionPolicy handleDel, - const Zstring& custDelFolder) : - handleDeletion(handleDel), - currentDelFolder(getSessionDeletionDir(custDelFolder)), //ends with path separator - txtMoveFileUserDefined( wxToZ(_("Moving file %x to user-defined directory %y")). Replace(DefaultStr("%x"), DefaultStr("\"%x\"\n"), false).Replace(DefaultStr("%y"), Zstring(DefaultStr("\"")) + custDelFolder + DefaultStr("\""), false)), - txtMoveFolderUserDefined(wxToZ(_("Moving folder %x to user-defined directory %y")).Replace(DefaultStr("%x"), DefaultStr("\"%x\"\n"), false).Replace(DefaultStr("%y"), Zstring(DefaultStr("\"")) + custDelFolder + DefaultStr("\""), false)) -{} + void copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& sourceFileSize) const; + void verifyFileCopy(const Zstring& source, const Zstring& target) const; + StatusHandler& statusUpdater_; +#ifdef FFS_WIN + ShadowCopy& shadowCopyHandler_; +#endif + const DeletionHandling& delHandling_; + + const bool copyFileSymLinks_; + const bool traverseDirSymLinks_; + const bool verifyCopiedFiles_; + + //preload status texts + const Zstring txtCopyingFile; + const Zstring txtOverwritingFile; + const Zstring txtCreatingFolder; + const Zstring txtDeletingFile; + const Zstring txtDeletingFolder; + const Zstring txtMoveToRecycler; + const Zstring txtVerifying; +}; + + class MoveFileCallbackImpl : public MoveFileCallback //callback functionality { public: - MoveFileCallbackImpl(StatusHandler* handler) : m_statusHandler(handler) {} + MoveFileCallbackImpl(StatusHandler& handler) : statusHandler_(handler) {} virtual Response requestUiRefresh() //DON'T throw exceptions here, at least in Windows build! { #ifdef FFS_WIN - m_statusHandler->requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this - if (m_statusHandler->abortIsRequested()) + statusHandler_.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this + if (statusHandler_.abortIsRequested()) return MoveFileCallback::CANCEL; #elif defined FFS_LINUX - m_statusHandler->requestUiRefresh(); //exceptions may be thrown here! + statusHandler_.requestUiRefresh(); //exceptions may be thrown here! #endif return MoveFileCallback::CONTINUE; } private: - StatusHandler* m_statusHandler; + StatusHandler& statusHandler_; }; template <FreeFileSync::SelectedSide side> inline -void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling& delHandling, bool showStatusUpdate) const +void SyncRecursively::removeFile(const FileMapping& fileObj, bool showStatusUpdate) const { Zstring statusText; - switch (delHandling.handleDeletion) + switch (delHandling_.handleDeletion) { case FreeFileSync::DELETE_PERMANENTLY: if (showStatusUpdate) //status information { statusText = txtDeletingFile; statusText.Replace(DefaultStr("%x"), fileObj.getFullName<side>(), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh } FreeFileSync::removeFile(fileObj.getFullName<side>(), false); break; @@ -433,8 +600,8 @@ void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling& { statusText = txtMoveToRecycler; statusText.Replace(DefaultStr("%x"), fileObj.getFullName<side>(), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh } FreeFileSync::removeFile(fileObj.getFullName<side>(), true); break; @@ -443,12 +610,12 @@ void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling& { if (showStatusUpdate) //status information { - statusText = delHandling.txtMoveFileUserDefined; + statusText = delHandling_.txtMoveFileUserDefined; statusText.Replace(DefaultStr("%x"), fileObj.getFullName<side>(), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh } - const Zstring targetFile = delHandling.currentDelFolder + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator + const Zstring targetFile = delHandling_.currentDelFolder + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator const Zstring targetDir = targetFile.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR); if (!FreeFileSync::dirExists(targetDir)) @@ -463,7 +630,7 @@ void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling& => setting NEEDS to be false: We want to move leaf, therefore symlinks in "some\dirs" must not interfere */ } - MoveFileCallbackImpl callBack(statusUpdater); //if file needs to be copied we need callback functionality to update screen and offer abort + MoveFileCallbackImpl callBack(statusUpdater_); //if file needs to be copied we need callback functionality to update screen and offer abort FreeFileSync::moveFile(fileObj.getFullName<side>(), targetFile, &callBack); } break; @@ -471,7 +638,7 @@ void SyncProcess::removeFile(const FileMapping& fileObj, const DeletionHandling& } -void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling& delHandling) const +void SyncRecursively::synchronizeFile(FileMapping& fileObj) const { Zstring statusText; Zstring target; @@ -484,8 +651,8 @@ void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling& statusText = txtCopyingFile; statusText.Replace(DefaultStr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false); statusText.Replace(DefaultStr("%y"), target.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target, fileObj.getFileSize<RIGHT_SIDE>()); break; @@ -496,28 +663,28 @@ void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling& statusText = txtCopyingFile; statusText.Replace(DefaultStr("%x"), fileObj.getShortName<LEFT_SIDE>(), false); statusText.Replace(DefaultStr("%y"), target.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target, fileObj.getFileSize<LEFT_SIDE>()); break; case SO_DELETE_LEFT: - removeFile<LEFT_SIDE>(fileObj, delHandling, true); //status updates in subroutine + removeFile<LEFT_SIDE>(fileObj, true); //status updates in subroutine break; case SO_DELETE_RIGHT: - removeFile<RIGHT_SIDE>(fileObj, delHandling, true); //status updates in subroutine + removeFile<RIGHT_SIDE>(fileObj, true); //status updates in subroutine break; case SO_OVERWRITE_RIGHT: statusText = txtOverwritingFile; statusText.Replace(DefaultStr("%x"), fileObj.getShortName<LEFT_SIDE>(), false); statusText.Replace(DefaultStr("%y"), fileObj.getFullName<RIGHT_SIDE>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - removeFile<RIGHT_SIDE>(fileObj, delHandling, false); + removeFile<RIGHT_SIDE>(fileObj, false); copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), fileObj.getFullName<RIGHT_SIDE>(), fileObj.getFileSize<LEFT_SIDE>()); break; @@ -525,10 +692,10 @@ void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling& statusText = txtOverwritingFile; statusText.Replace(DefaultStr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false); statusText.Replace(DefaultStr("%y"), fileObj.getFullName<LEFT_SIDE>().BeforeLast(globalFunctions::FILE_NAME_SEPARATOR), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - removeFile<LEFT_SIDE>(fileObj, delHandling, false); + removeFile<LEFT_SIDE>(fileObj, false); copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), fileObj.getFullName<LEFT_SIDE>(), fileObj.getFileSize<RIGHT_SIDE>()); break; @@ -542,24 +709,24 @@ void SyncProcess::synchronizeFile(FileMapping& fileObj, const DeletionHandling& //progress indicator update //indicator is updated only if file is sync'ed correctly (and if some sync was done)! - statusUpdater->updateProcessedData(1, 0); //processed data is communicated in subfunctions! + statusUpdater_.updateProcessedData(1, 0); //processed data is communicated in subfunctions! } template <FreeFileSync::SelectedSide side> inline -void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling& delHandling) const +void SyncRecursively::removeFolder(const DirMapping& dirObj) const { Zstring statusText; - switch (delHandling.handleDeletion) + switch (delHandling_.handleDeletion) { case FreeFileSync::DELETE_PERMANENTLY: //status information statusText = txtDeletingFolder; statusText.Replace(DefaultStr("%x"), dirObj.getFullName<side>(), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh FreeFileSync::removeDirectory(dirObj.getFullName<side>(), false); break; @@ -567,8 +734,8 @@ void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling& //status information statusText = txtMoveToRecycler; statusText.Replace(DefaultStr("%x"), dirObj.getFullName<side>(), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh FreeFileSync::removeDirectory(dirObj.getFullName<side>(), true); break; @@ -576,12 +743,12 @@ void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling& if (FreeFileSync::dirExists(dirObj.getFullName<side>())) { //status information - statusText = delHandling.txtMoveFolderUserDefined; + statusText = delHandling_.txtMoveFolderUserDefined; statusText.Replace(DefaultStr("%x"), dirObj.getFullName<side>(), false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - const Zstring targetDir = delHandling.currentDelFolder + dirObj.getRelativeName<side>(); + const Zstring targetDir = delHandling_.currentDelFolder + dirObj.getRelativeName<side>(); const Zstring targetSuperDir = targetDir.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR);; if (!FreeFileSync::dirExists(targetSuperDir)) @@ -596,7 +763,7 @@ void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling& => setting NEEDS to be false: We want to move leaf, therefore symlinks in "some\dirs" must not interfere */ } - MoveFileCallbackImpl callBack(statusUpdater); //if files need to be copied, we need callback functionality to update screen and offer abort + MoveFileCallbackImpl callBack(statusUpdater_); //if files need to be copied, we need callback functionality to update screen and offer abort FreeFileSync::moveDirectory(dirObj.getFullName<side>(), targetDir, true, &callBack); } break; @@ -604,7 +771,7 @@ void SyncProcess::removeFolder(const DirMapping& dirObj, const DeletionHandling& } -void SyncProcess::synchronizeFolder(DirMapping& dirObj, const DeletionHandling& delHandling) const +void SyncRecursively::synchronizeFolder(DirMapping& dirObj) const { Zstring statusText; Zstring target; @@ -617,13 +784,13 @@ void SyncProcess::synchronizeFolder(DirMapping& dirObj, const DeletionHandling& statusText = txtCreatingFolder; statusText.Replace(DefaultStr("%x"), target, false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh //some check to catch the error that directory on source has been deleted externally after "compare"... if (!FreeFileSync::dirExists(dirObj.getFullName<RIGHT_SIDE>())) throw FileError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(dirObj.getFullName<RIGHT_SIDE>()) + wxT("\"")); - createDirectory(target, dirObj.getFullName<RIGHT_SIDE>(), !m_traverseDirSymLinks); //traverse symlinks <=> !copy symlinks + createDirectory(target, dirObj.getFullName<RIGHT_SIDE>(), !traverseDirSymLinks_); //traverse symlinks <=> !copy symlinks break; case SO_CREATE_NEW_RIGHT: @@ -631,36 +798,36 @@ void SyncProcess::synchronizeFolder(DirMapping& dirObj, const DeletionHandling& statusText = txtCreatingFolder; statusText.Replace(DefaultStr("%x"), target, false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh //some check to catch the error that directory on source has been deleted externally after "compare"... if (!FreeFileSync::dirExists(dirObj.getFullName<LEFT_SIDE>())) throw FileError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(dirObj.getFullName<LEFT_SIDE>()) + wxT("\"")); - createDirectory(target, dirObj.getFullName<LEFT_SIDE>(), !m_traverseDirSymLinks); //traverse symlinks <=> !copy symlinks + createDirectory(target, dirObj.getFullName<LEFT_SIDE>(), !traverseDirSymLinks_); //traverse symlinks <=> !copy symlinks break; case SO_DELETE_LEFT: - removeFolder<LEFT_SIDE>(dirObj, delHandling); + removeFolder<LEFT_SIDE>(dirObj); { //progress indicator update: DON'T forget to notify about implicitly deleted objects! const SyncStatistics subObjects(dirObj); //...then remove everything dirObj.subFiles.clear(); dirObj.subDirs.clear(); - statusUpdater->updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble()); + statusUpdater_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble()); } break; case SO_DELETE_RIGHT: - removeFolder<RIGHT_SIDE>(dirObj, delHandling); + removeFolder<RIGHT_SIDE>(dirObj); { //progress indicator update: DON'T forget to notify about implicitly deleted objects! const SyncStatistics subObjects(dirObj); //...then remove everything dirObj.subFiles.clear(); dirObj.subDirs.clear(); - statusUpdater->updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble()); + statusUpdater_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble()); } break; @@ -677,146 +844,7 @@ void SyncProcess::synchronizeFolder(DirMapping& dirObj, const DeletionHandling& //progress indicator update //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 -} - - -inline -bool deletionImminent(const FileSystemObject& fsObj) -{ - //test if current sync-line will result in deletion of files -> used to avoid disc space bottlenecks - const SyncOperation op = fsObj.getSyncOperation(); - return op == FreeFileSync::SO_DELETE_LEFT || op == FreeFileSync::SO_DELETE_RIGHT; -} - - -class RemoveInvalid -{ -public: - RemoveInvalid(HierarchyObject& hierObj) : - hierObj_(hierObj) {} - - ~RemoveInvalid() - { - FileSystemObject::removeEmptyNonRec(hierObj_); - } - -private: - HierarchyObject& hierObj_; -}; - - -template <bool deleteOnly> //"true" if files deletion shall happen only -class SyncProcess::SyncRecursively -{ -public: - SyncRecursively(const SyncProcess* const syncProc, const SyncProcess::DeletionHandling& delHandling) : - syncProc_(syncProc), - delHandling_(delHandling) {} - - void execute(HierarchyObject& hierObj) - { - //enforce removal of invalid entries (where both sides are empty) - RemoveInvalid dummy(hierObj); //non-recursive - - //synchronize files: - for (HierarchyObject::SubFileMapping::iterator i = hierObj.subFiles.begin(); i != hierObj.subFiles.end(); ++i) - { - if ( ( deleteOnly && deletionImminent(*i)) || - (!deleteOnly && !deletionImminent(*i))) - { - while (true) - { - try - { - syncProc_->synchronizeFile(*i, delHandling_); - break; - } - catch (FileError& error) - { - //User abort when copying files or moving files/directories into custom deletion directory: - //windows build: abort if requested, don't show error message if cancelled by user! - //linux build: this refresh is not necessary, because user abort triggers an AbortThisProcess() exception without a FileError() - syncProc_->statusUpdater->requestUiRefresh(true); - - ErrorHandler::Response rv = syncProc_->statusUpdater->reportError(error.show()); - if ( rv == ErrorHandler::IGNORE_ERROR) - break; - else if (rv == ErrorHandler::RETRY) - ; //continue with loop - else - throw std::logic_error("Programming Error: Unknown return value!"); - } - } - } - } - - //synchronize folders: - for (HierarchyObject::SubDirMapping::iterator i = hierObj.subDirs.begin(); i != hierObj.subDirs.end(); ++i) - { - if (deleteOnly) //no need, to process folders more than once! - { - while (true) - { - try - { - syncProc_->synchronizeFolder(*i, delHandling_); - break; - } - catch (FileError& error) - { - //User abort when copying files or moving files/directories into custom deletion directory: - //windows build: abort if requested, don't show error message if cancelled by user! - //linux build: this refresh is not necessary, because user abort triggers an AbortThisProcess() exception without a FileError() - syncProc_->statusUpdater->requestUiRefresh(true); - - ErrorHandler::Response rv = syncProc_->statusUpdater->reportError(error.show()); - if (rv == ErrorHandler::IGNORE_ERROR) - break; - else if (rv == ErrorHandler::RETRY) - ; //continue with loop - else - throw std::logic_error("Programming Error: Unknown return value!"); - } - } - } - - //recursive synchronization: - //if (!i->isEmpty()) -> not necessary, deleted folders have no sub-objects at this point - execute(*i); - } - } - -private: - const SyncProcess* const syncProc_; - const SyncProcess::DeletionHandling& delHandling_; -}; - - -template <typename Function> -inline -void tryReportingError(StatusHandler& handler, Function cmd) -{ - while (true) - { - try - { - cmd(); - break; - } - catch (FileError& error) - { - handler.requestUiRefresh(); //may throw! - - ErrorHandler::Response rv = handler.reportError(error.show()); //may throw! - if ( rv == ErrorHandler::IGNORE_ERROR) - break; - else if (rv == ErrorHandler::RETRY) - ; //continue with loop - else - throw std::logic_error("Programming Error: Unknown return value!"); - } - } + statusUpdater_.updateProcessedData(1, 0); //each call represents one processed file } @@ -830,9 +858,8 @@ public: //update sync database after synchronization is finished void updateNow() { - //these calls may throw! - tryReportingError(statusHandler_, boost::bind(saveToDisk, boost::cref(baseMap_), LEFT_SIDE, baseMap_.getDBFilename<LEFT_SIDE>())); - tryReportingError(statusHandler_, boost::bind(saveToDisk, boost::cref(baseMap_), RIGHT_SIDE, baseMap_.getDBFilename<RIGHT_SIDE>())); + //these calls may throw in error-callbacks! + tryReportingError(statusHandler_, boost::bind(saveToDisk, boost::cref(baseMap_))); }; //_("You can ignore the error to skip current folder pair.")); @@ -864,9 +891,9 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf const SyncStatistics statisticsTotal(folderCmp); //keep at beginning so that all gui elements are initialized properly - statusUpdater->initNewProcess(statisticsTotal.getCreate() + statisticsTotal.getOverwrite() + statisticsTotal.getDelete(), - globalFunctions::convertToSigned(statisticsTotal.getDataToProcess()), - StatusHandler::PROCESS_SYNCHRONIZING); + statusUpdater.initNewProcess(statisticsTotal.getCreate() + statisticsTotal.getOverwrite() + statisticsTotal.getDelete(), + globalFunctions::convertToSigned(statisticsTotal.getDataToProcess()), + StatusHandler::PROCESS_SYNCHRONIZING); //-------------------some basic checks:------------------------------------------ @@ -883,7 +910,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf //test existence of Recycle Bin if (folderPairCfg.handleDeletion == FreeFileSync::MOVE_TO_RECYCLE_BIN && !FreeFileSync::recycleBinExists()) { - statusUpdater->reportFatalError(_("Unable to initialize Recycle Bin!")); + statusUpdater.reportFatalError(_("Unable to initialize Recycle Bin!")); return; //should be obsolete! } @@ -892,7 +919,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf //check if user-defined directory for deletion was specified if (FreeFileSync::getFormattedDirectoryName(folderPairCfg.custDelFolder.c_str()).empty()) { - statusUpdater->reportFatalError(_("User-defined directory for deletion was not specified!")); + statusUpdater.reportFatalError(_("User-defined directory for deletion was not specified!")); return; //should be obsolete! } } @@ -901,23 +928,23 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf //avoid data loss when source directory doesn't (temporarily?) exist anymore AND user chose to ignore errors(else we wouldn't arrive here) if (dataLossPossible(j->getBaseDir<LEFT_SIDE>(), statisticsFolderPair)) { - statusUpdater->reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"")); + statusUpdater.reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"")); return; //should be obsolete! } if (dataLossPossible(j->getBaseDir<RIGHT_SIDE>(), statisticsFolderPair)) { - statusUpdater->reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"") ); + statusUpdater.reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"") ); return; //should be obsolete! } //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted if (significantDifferenceDetected(statisticsFolderPair)) { - statusUpdater->reportWarning(wxString(_("Significant difference detected:")) + wxT("\n") + - zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT(" <-> ") + wxT("\n") + - zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\n\n") + - _("More than 50% of the total number of files will be copied or deleted!"), - m_warnings.warningSignificantDifference); + statusUpdater.reportWarning(wxString(_("Significant difference detected:")) + wxT("\n") + + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT(" <-> ") + wxT("\n") + + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\n\n") + + _("More than 50% of the total number of files will be copied or deleted!"), + m_warnings.warningSignificantDifference); } //check for sufficient free diskspace in left directory @@ -927,11 +954,11 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf if (wxGetDiskSpace(zToWx(j->getBaseDir<LEFT_SIDE>()), NULL, &freeDiskSpaceLeft)) { if (freeDiskSpaceLeft < spaceNeeded.first) - statusUpdater->reportWarning(wxString(_("Not enough free disk space available in:")) + wxT("\n") + - wxT("\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"\n\n") + - _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.first) + wxT("\n") + - _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(freeDiskSpaceLeft), - m_warnings.warningNotEnoughDiskSpace); + statusUpdater.reportWarning(wxString(_("Not enough free disk space available in:")) + wxT("\n") + + wxT("\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"\n\n") + + _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.first) + wxT("\n") + + _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(freeDiskSpaceLeft), + m_warnings.warningNotEnoughDiskSpace); } //check for sufficient free diskspace in right directory @@ -939,21 +966,25 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf if (wxGetDiskSpace(zToWx(j->getBaseDir<RIGHT_SIDE>()), NULL, &freeDiskSpaceRight)) { if (freeDiskSpaceRight < spaceNeeded.second) - statusUpdater->reportWarning(wxString(_("Not enough free disk space available in:")) + wxT("\n") + - wxT("\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"\n\n") + - _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.second) + wxT("\n") + - _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(freeDiskSpaceRight), - m_warnings.warningNotEnoughDiskSpace); + statusUpdater.reportWarning(wxString(_("Not enough free disk space available in:")) + wxT("\n") + + wxT("\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"\n\n") + + _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(spaceNeeded.second) + wxT("\n") + + _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(freeDiskSpaceRight), + m_warnings.warningNotEnoughDiskSpace); } } //check if unresolved conflicts exist if (statisticsTotal.getConflict() > 0) - statusUpdater->reportWarning(_("Unresolved conflicts existing! \n\nYou can ignore conflicts and continue synchronization."), - m_warnings.warningUnresolvedConflicts); + statusUpdater.reportWarning(_("Unresolved conflicts existing! \n\nYou can ignore conflicts and continue synchronization."), + m_warnings.warningUnresolvedConflicts); //-------------------end of basic checks------------------------------------------ +#ifdef FFS_WIN + //shadow copy buffer: per sync-instance, not folder pair + std::auto_ptr<ShadowCopy> shadowCopyHandler(new ShadowCopy); +#endif try { @@ -970,23 +1001,31 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf //execute synchronization recursively //loop through all files twice; reason: first delete, then copy - SyncRecursively<true>( this, currentDelHandling).execute(*j); - SyncRecursively<false>(this, currentDelHandling).execute(*j); + SyncRecursively( *this, +#ifdef FFS_WIN + *shadowCopyHandler, +#endif + currentDelHandling).execute<true>(*j); + SyncRecursively(*this, +#ifdef FFS_WIN + *shadowCopyHandler, +#endif + currentDelHandling).execute<false>(*j); //------------------------------------------------------------------------------------------ //update synchronization database (automatic sync only) if (folderPairCfg.updateSyncDB) { - UpdateDatabase syncDB(*j, *statusUpdater); - statusUpdater->updateStatusText(wxToZ(_("Generating database..."))); - statusUpdater->forceUiRefresh(); + UpdateDatabase syncDB(*j, statusUpdater); + statusUpdater.updateStatusText(wxToZ(_("Generating database..."))); + statusUpdater.forceUiRefresh(); syncDB.updateNow(); } } } catch (const std::exception& e) { - statusUpdater->reportFatalError(wxString::FromAscii(e.what())); + statusUpdater.reportFatalError(wxString::FromAscii(e.what())); return; //should be obsolete! } } @@ -999,7 +1038,7 @@ class WhileCopying : public FreeFileSync::CopyFileCallback //callback functional { public: - WhileCopying(wxLongLong& bytesTransferredLast, StatusHandler* statusHandler) : + WhileCopying(wxLongLong& bytesTransferredLast, StatusHandler& statusHandler) : m_bytesTransferredLast(bytesTransferredLast), m_statusHandler(statusHandler) {} @@ -1009,27 +1048,27 @@ public: const wxLongLong totalBytes = globalFunctions::convertToSigned(totalBytesTransferred); //inform about the (differential) processed amount of data - m_statusHandler->updateProcessedData(0, totalBytes - m_bytesTransferredLast); + m_statusHandler.updateProcessedData(0, totalBytes - m_bytesTransferredLast); m_bytesTransferredLast = totalBytes; #ifdef FFS_WIN - m_statusHandler->requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this - if (m_statusHandler->abortIsRequested()) + m_statusHandler.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this + if (m_statusHandler.abortIsRequested()) return CopyFileCallback::CANCEL; #elif defined FFS_LINUX - m_statusHandler->requestUiRefresh(); //exceptions may be thrown here! + m_statusHandler.requestUiRefresh(); //exceptions may be thrown here! #endif return CopyFileCallback::CONTINUE; } private: wxLongLong& m_bytesTransferredLast; - StatusHandler* m_statusHandler; + StatusHandler& m_statusHandler; }; //copy file while executing statusUpdater->requestUiRefresh() calls -void SyncProcess::copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& totalBytesToCpy) const +void SyncRecursively::copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& totalBytesToCpy) const { //create folders first (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080) const Zstring targetDir = target.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR); @@ -1046,28 +1085,28 @@ void SyncProcess::copyFileUpdating(const Zstring& source, const Zstring& target, //start of (possibly) long-running copy process: ensure status updates are performed regularly wxLongLong totalBytesTransferred; - WhileCopying callback(totalBytesTransferred, statusUpdater); + WhileCopying callback(totalBytesTransferred, statusUpdater_); try { #ifdef FFS_WIN - FreeFileSync::copyFile(source, target, m_copyFileSymLinks, shadowCopyHandler.get(), &callback); + FreeFileSync::copyFile(source, target, copyFileSymLinks_, &shadowCopyHandler_, &callback); #elif defined FFS_LINUX - FreeFileSync::copyFile(source, target, m_copyFileSymLinks, &callback); + FreeFileSync::copyFile(source, target, copyFileSymLinks_, &callback); #endif - if (m_verifyCopiedFiles) //verify if data was copied correctly + if (verifyCopiedFiles_) //verify if data was copied correctly verifyFileCopy(source, target); } catch (...) { //error situation: undo communication of processed amount of data - statusUpdater->updateProcessedData(0, totalBytesTransferred * -1 ); + statusUpdater_.updateProcessedData(0, totalBytesTransferred * -1 ); throw; } //inform about the (remaining) processed amount of data - statusUpdater->updateProcessedData(0, globalFunctions::convertToSigned(totalBytesToCpy) - totalBytesTransferred); + statusUpdater_.updateProcessedData(0, globalFunctions::convertToSigned(totalBytesToCpy) - totalBytesTransferred); } @@ -1134,33 +1173,33 @@ void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback* c class VerifyStatusUpdater : public VerifyCallback { public: - VerifyStatusUpdater(StatusHandler* statusHandler) : m_statusHandler(statusHandler) {} + VerifyStatusUpdater(StatusHandler& statusHandler) : statusHandler_(statusHandler) {} virtual void updateStatus() { - m_statusHandler->requestUiRefresh(); //trigger display refresh + statusHandler_.requestUiRefresh(); //trigger display refresh } private: - StatusHandler* m_statusHandler; + StatusHandler& statusHandler_; }; -void SyncProcess::verifyFileCopy(const Zstring& source, const Zstring& target) const +void SyncRecursively::verifyFileCopy(const Zstring& source, const Zstring& target) const { Zstring statusText = txtVerifying; statusText.Replace(DefaultStr("%x"), target, false); - statusUpdater->updateStatusText(statusText); - statusUpdater->requestUiRefresh(); //trigger display refresh + statusUpdater_.updateStatusText(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - VerifyStatusUpdater callback(statusUpdater); + VerifyStatusUpdater callback(statusUpdater_); try { verifyFiles(source, target, &callback); } catch (FileError& error) { - switch (statusUpdater->reportError(error.show())) + switch (statusUpdater_.reportError(error.show())) { case ErrorHandler::IGNORE_ERROR: break; @@ -1176,3 +1215,6 @@ void SyncProcess::verifyFileCopy(const Zstring& source, const Zstring& target) c + + + |