diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:10:11 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:10:11 +0200 |
commit | c0cdb2ad99a1e2a6ade5ce76c91177a79258e669 (patch) | |
tree | 4701a015385d9a6a5a4ba99a8f1f5d400fff26b1 /synchronization.cpp | |
parent | 3.13 (diff) | |
download | FreeFileSync-c0cdb2ad99a1e2a6ade5ce76c91177a79258e669.tar.gz FreeFileSync-c0cdb2ad99a1e2a6ade5ce76c91177a79258e669.tar.bz2 FreeFileSync-c0cdb2ad99a1e2a6ade5ce76c91177a79258e669.zip |
3.14
Diffstat (limited to 'synchronization.cpp')
-rw-r--r-- | synchronization.cpp | 1249 |
1 files changed, 653 insertions, 596 deletions
diff --git a/synchronization.cpp b/synchronization.cpp index 8e0dc7de..77b734eb 100644 --- a/synchronization.cpp +++ b/synchronization.cpp @@ -23,6 +23,8 @@ #include "library/db_file.h" #include "shared/disable_standby.h" #include "library/cmp_filetime.h" +#include "shared/file_io.h" +#include <deque> #ifdef FFS_WIN #include "shared/long_path_prefix.h" @@ -108,51 +110,51 @@ void SyncStatistics::getFileNumbers(const FileMapping& fileObj) { switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction { - case SO_CREATE_NEW_LEFT: - ++createLeft; - dataToProcess += fileObj.getFileSize<RIGHT_SIDE>(); - break; + case SO_CREATE_NEW_LEFT: + ++createLeft; + dataToProcess += fileObj.getFileSize<RIGHT_SIDE>(); + break; - case SO_CREATE_NEW_RIGHT: - ++createRight; - dataToProcess += fileObj.getFileSize<LEFT_SIDE>(); - break; + case SO_CREATE_NEW_RIGHT: + ++createRight; + dataToProcess += fileObj.getFileSize<LEFT_SIDE>(); + break; - case SO_DELETE_LEFT: - ++deleteLeft; - break; + case SO_DELETE_LEFT: + ++deleteLeft; + break; - case SO_DELETE_RIGHT: - ++deleteRight; - break; + case SO_DELETE_RIGHT: + ++deleteRight; + break; - case SO_OVERWRITE_LEFT: - ++overwriteLeft; - dataToProcess += fileObj.getFileSize<RIGHT_SIDE>(); - break; + case SO_OVERWRITE_LEFT: + ++overwriteLeft; + dataToProcess += fileObj.getFileSize<RIGHT_SIDE>(); + break; - case SO_OVERWRITE_RIGHT: - ++overwriteRight; - dataToProcess += fileObj.getFileSize<LEFT_SIDE>(); - break; + case SO_OVERWRITE_RIGHT: + ++overwriteRight; + dataToProcess += fileObj.getFileSize<LEFT_SIDE>(); + break; - case SO_UNRESOLVED_CONFLICT: - ++conflict; - if (firstConflicts.size() < 3) //save the first 3 conflict texts - firstConflicts.push_back(std::make_pair(fileObj.getObjRelativeName(), fileObj.getSyncOpConflict())); - break; + case SO_UNRESOLVED_CONFLICT: + ++conflict; + if (firstConflicts.size() < 3) //save the first 3 conflict texts + firstConflicts.push_back(std::make_pair(fileObj.getObjRelativeName(), fileObj.getSyncOpConflict())); + break; - case SO_COPY_METADATA_TO_LEFT: - ++overwriteLeft; - break; + case SO_COPY_METADATA_TO_LEFT: + ++overwriteLeft; + break; - case SO_COPY_METADATA_TO_RIGHT: - ++overwriteRight; - break; + case SO_COPY_METADATA_TO_RIGHT: + ++overwriteRight; + break; - case SO_DO_NOTHING: - case SO_EQUAL: - break; + case SO_DO_NOTHING: + case SO_EQUAL: + break; } } @@ -162,41 +164,41 @@ void SyncStatistics::getLinkNumbers(const SymLinkMapping& linkObj) { switch (linkObj.getSyncOperation()) //evaluate comparison result and sync direction { - case SO_CREATE_NEW_LEFT: - ++createLeft; - break; + case SO_CREATE_NEW_LEFT: + ++createLeft; + break; - case SO_CREATE_NEW_RIGHT: - ++createRight; - break; + case SO_CREATE_NEW_RIGHT: + ++createRight; + break; - case SO_DELETE_LEFT: - ++deleteLeft; - break; + case SO_DELETE_LEFT: + ++deleteLeft; + break; - case SO_DELETE_RIGHT: - ++deleteRight; - break; + case SO_DELETE_RIGHT: + ++deleteRight; + break; - case SO_OVERWRITE_LEFT: - case SO_COPY_METADATA_TO_LEFT: - ++overwriteLeft; - break; + case SO_OVERWRITE_LEFT: + case SO_COPY_METADATA_TO_LEFT: + ++overwriteLeft; + break; - case SO_OVERWRITE_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: - ++overwriteRight; - break; + case SO_OVERWRITE_RIGHT: + case SO_COPY_METADATA_TO_RIGHT: + ++overwriteRight; + break; - case SO_UNRESOLVED_CONFLICT: - ++conflict; - if (firstConflicts.size() < 3) //save the first 3 conflict texts - firstConflicts.push_back(std::make_pair(linkObj.getObjRelativeName(), linkObj.getSyncOpConflict())); - break; + case SO_UNRESOLVED_CONFLICT: + ++conflict; + if (firstConflicts.size() < 3) //save the first 3 conflict texts + firstConflicts.push_back(std::make_pair(linkObj.getObjRelativeName(), linkObj.getSyncOpConflict())); + break; - case SO_DO_NOTHING: - case SO_EQUAL: - break; + case SO_DO_NOTHING: + case SO_EQUAL: + break; } } @@ -206,44 +208,44 @@ void SyncStatistics::getDirNumbers(const DirMapping& dirObj) { switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction { - case SO_CREATE_NEW_LEFT: - ++createLeft; - break; + case SO_CREATE_NEW_LEFT: + ++createLeft; + break; - case SO_CREATE_NEW_RIGHT: - ++createRight; - break; + case SO_CREATE_NEW_RIGHT: + ++createRight; + break; - case SO_DELETE_LEFT: - ++deleteLeft; - break; + case SO_DELETE_LEFT: + ++deleteLeft; + break; - case SO_DELETE_RIGHT: - ++deleteRight; - break; + case SO_DELETE_RIGHT: + ++deleteRight; + break; - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - assert(false); - break; + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + assert(false); + break; - case SO_UNRESOLVED_CONFLICT: - ++conflict; - if (firstConflicts.size() < 3) //save the first 3 conflict texts - firstConflicts.push_back(std::make_pair(dirObj.getObjRelativeName(), dirObj.getSyncOpConflict())); - break; + case SO_UNRESOLVED_CONFLICT: + ++conflict; + if (firstConflicts.size() < 3) //save the first 3 conflict texts + firstConflicts.push_back(std::make_pair(dirObj.getObjRelativeName(), dirObj.getSyncOpConflict())); + break; - case SO_COPY_METADATA_TO_LEFT: - ++overwriteLeft; - break; + case SO_COPY_METADATA_TO_LEFT: + ++overwriteLeft; + break; - case SO_COPY_METADATA_TO_RIGHT: - ++overwriteRight; - break; + case SO_COPY_METADATA_TO_RIGHT: + ++overwriteRight; + break; - case SO_DO_NOTHING: - case SO_EQUAL: - break; + case SO_DO_NOTHING: + case SO_EQUAL: + break; } //recurse into sub-dirs @@ -305,42 +307,42 @@ private: for (HierarchyObject::SubFileMapping::const_iterator i = hierObj.useSubFiles().begin(); i != hierObj.useSubFiles().end(); ++i) switch (i->getSyncOperation()) //evaluate comparison result and sync direction { - case SO_CREATE_NEW_LEFT: - spaceNeededLeft += common::convertToSigned(i->getFileSize<RIGHT_SIDE>()); - break; - - case SO_CREATE_NEW_RIGHT: - spaceNeededRight += common::convertToSigned(i->getFileSize<LEFT_SIDE>()); - break; - - case SO_DELETE_LEFT: - if (freeSpaceDelLeft_) - spaceNeededLeft -= common::convertToSigned(i->getFileSize<LEFT_SIDE>()); - break; - - case SO_DELETE_RIGHT: - if (freeSpaceDelRight_) - spaceNeededRight -= common::convertToSigned(i->getFileSize<RIGHT_SIDE>()); - break; - - case SO_OVERWRITE_LEFT: - if (freeSpaceDelLeft_) - spaceNeededLeft -= common::convertToSigned(i->getFileSize<LEFT_SIDE>()); - spaceNeededLeft += common::convertToSigned(i->getFileSize<RIGHT_SIDE>()); - break; - - case SO_OVERWRITE_RIGHT: - if (freeSpaceDelRight_) - spaceNeededRight -= common::convertToSigned(i->getFileSize<RIGHT_SIDE>()); - spaceNeededRight += common::convertToSigned(i->getFileSize<LEFT_SIDE>()); - break; - - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - break; + case SO_CREATE_NEW_LEFT: + spaceNeededLeft += common::convertToSigned(i->getFileSize<RIGHT_SIDE>()); + break; + + case SO_CREATE_NEW_RIGHT: + spaceNeededRight += common::convertToSigned(i->getFileSize<LEFT_SIDE>()); + break; + + case SO_DELETE_LEFT: + if (freeSpaceDelLeft_) + spaceNeededLeft -= common::convertToSigned(i->getFileSize<LEFT_SIDE>()); + break; + + case SO_DELETE_RIGHT: + if (freeSpaceDelRight_) + spaceNeededRight -= common::convertToSigned(i->getFileSize<RIGHT_SIDE>()); + break; + + case SO_OVERWRITE_LEFT: + if (freeSpaceDelLeft_) + spaceNeededLeft -= common::convertToSigned(i->getFileSize<LEFT_SIDE>()); + spaceNeededLeft += common::convertToSigned(i->getFileSize<RIGHT_SIDE>()); + break; + + case SO_OVERWRITE_RIGHT: + if (freeSpaceDelRight_) + spaceNeededRight -= common::convertToSigned(i->getFileSize<RIGHT_SIDE>()); + spaceNeededRight += common::convertToSigned(i->getFileSize<LEFT_SIDE>()); + break; + + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + break; } //symbolic links @@ -365,20 +367,20 @@ bool deletionFreesSpace(const Zstring& baseDir, { switch (handleDeletion) { - case DELETE_PERMANENTLY: - return true; - case MOVE_TO_RECYCLE_BIN: - return false; //in general... (unless Recycle Bin is full) - case MOVE_TO_CUSTOM_DIRECTORY: - switch (ffs3::onSameVolume(baseDir, custDelFolderFmt)) - { - case VOLUME_SAME: - return false; - case VOLUME_DIFFERENT: - return true; //but other volume (custDelFolderFmt) may become full... - case VOLUME_CANT_SAY: - return true; //a rough guess! - } + case DELETE_PERMANENTLY: + return true; + case MOVE_TO_RECYCLE_BIN: + return false; //in general... (unless Recycle Bin is full) + case MOVE_TO_CUSTOM_DIRECTORY: + switch (ffs3::onSameVolume(baseDir, custDelFolderFmt)) + { + case VOLUME_SAME: + return false; + case VOLUME_DIFFERENT: + return true; //but other volume (custDelFolderFmt) may become full... + case VOLUME_CANT_SAY: + return true; //a rough guess! + } } assert(false); return true; @@ -411,25 +413,18 @@ bool synchronizationNeeded(const SyncStatistics& statisticsTotal) } -bool ffs3::synchronizationNeeded(const FolderComparison& folderCmp) -{ - const SyncStatistics statisticsTotal(folderCmp); - return ::synchronizationNeeded(statisticsTotal); -} - - //test if user accidentally tries to sync the wrong folders bool significantDifferenceDetected(const SyncStatistics& folderPairStat) { //initial file copying shall not be detected as major difference - if ( folderPairStat.getCreate<LEFT_SIDE>() == 0 && - folderPairStat.getOverwrite() == 0 && - folderPairStat.getDelete() == 0 && - folderPairStat.getConflict() == 0) return false; - if ( folderPairStat.getCreate<RIGHT_SIDE>() == 0 && - folderPairStat.getOverwrite() == 0 && - folderPairStat.getDelete() == 0 && - folderPairStat.getConflict() == 0) return false; + if (folderPairStat.getCreate<LEFT_SIDE>() == 0 && + folderPairStat.getOverwrite() == 0 && + folderPairStat.getDelete() == 0 && + folderPairStat.getConflict() == 0) return false; + if (folderPairStat.getCreate<RIGHT_SIDE>() == 0 && + folderPairStat.getOverwrite() == 0 && + folderPairStat.getDelete() == 0 && + folderPairStat.getConflict() == 0) return false; const int changedRows = folderPairStat.getCreate() + folderPairStat.getOverwrite() + @@ -452,14 +447,14 @@ FolderPairSyncCfg::FolderPairSyncCfg(bool automaticMode, template <typename Function> inline -void tryReportingError(StatusHandler& handler, Function cmd) +bool tryReportingError(StatusHandler& handler, Function cmd) //return "true" on success, "false" if error was ignored { while (true) { try { cmd(); - break; + return true; } catch (FileError& error) { @@ -469,8 +464,8 @@ void tryReportingError(StatusHandler& handler, Function cmd) handler.requestUiRefresh(true); //may throw! ErrorHandler::Response rv = handler.reportError(error.msg()); //may throw! - if ( rv == ErrorHandler::IGNORE_ERROR) - break; + if (rv == ErrorHandler::IGNORE_ERROR) + return false; else if (rv == ErrorHandler::RETRY) ; //continue with loop else @@ -596,26 +591,26 @@ DeletionHandling::DeletionHandling(const DeletionPolicy handleDel, { switch (handleDel) { - case DELETE_PERMANENTLY: - txtRemovingFile = wxToZ(_("Deleting file %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false); - txtRemovingSymlink = wxToZ(_("Deleting Symbolic Link %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false); - txtRemovingDirectory = wxToZ(_("Deleting folder %x")).Replace( Zstr("%x"), Zstr("\n\"%x\""), false); - break; + case DELETE_PERMANENTLY: + txtRemovingFile = wxToZ(_("Deleting file %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false); + txtRemovingSymlink = wxToZ(_("Deleting Symbolic Link %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false); + txtRemovingDirectory = wxToZ(_("Deleting folder %x")).Replace( Zstr("%x"), Zstr("\n\"%x\""), false); + break; - case MOVE_TO_RECYCLE_BIN: - sessionDelDirLeft = getSessionDeletionDir(baseDirLeft, Zstr("FFS ")); - sessionDelDirRight = getSessionDeletionDir(baseDirRight, Zstr("FFS ")); + case MOVE_TO_RECYCLE_BIN: + sessionDelDirLeft = getSessionDeletionDir(baseDirLeft, Zstr("FFS ")); + sessionDelDirRight = getSessionDeletionDir(baseDirRight, Zstr("FFS ")); - txtRemovingFile = txtRemovingSymlink = txtRemovingDirectory = wxToZ(_("Moving %x to Recycle Bin")).Replace(Zstr("%x"), Zstr("\"%x\""), false); - break; + txtRemovingFile = txtRemovingSymlink = txtRemovingDirectory = wxToZ(_("Moving %x to Recycle Bin")).Replace(Zstr("%x"), Zstr("\"%x\""), false); + break; - case MOVE_TO_CUSTOM_DIRECTORY: - sessionDelDirLeft = sessionDelDirRight = getSessionDeletionDir(custDelFolder); + case MOVE_TO_CUSTOM_DIRECTORY: + sessionDelDirLeft = sessionDelDirRight = getSessionDeletionDir(custDelFolder); - txtRemovingFile = wxToZ(_("Moving file %x to user-defined directory %y")). Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false); - txtRemovingDirectory = wxToZ(_("Moving folder %x to user-defined directory %y")). Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false); - txtRemovingSymlink = wxToZ(_("Moving Symbolic Link %x to user-defined directory %y")).Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false); - break; + txtRemovingFile = wxToZ(_("Moving file %x to user-defined directory %y")). Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false); + txtRemovingDirectory = wxToZ(_("Moving folder %x to user-defined directory %y")). Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false); + txtRemovingSymlink = wxToZ(_("Moving Symbolic Link %x to user-defined directory %y")).Replace(Zstr("%x"), Zstr("\"%x\"\n"), false).Replace(Zstr("%y"), Zstring(Zstr("\"")) + custDelFolder + Zstr("\""), false); + break; } } @@ -739,46 +734,46 @@ void DeletionHandling::removeFile(const FileSystemObject& fileObj) const switch (deletionType) { - case DELETE_PERMANENTLY: - ffs3::removeFile(fileObj.getFullName<side>()); - break; + case DELETE_PERMANENTLY: + ffs3::removeFile(fileObj.getFullName<side>()); + break; - case MOVE_TO_RECYCLE_BIN: - if (fileExists(fileObj.getFullName<side>())) - { - const Zstring targetFile = getSessionDir<side>() + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator - const Zstring targetDir = targetFile.BeforeLast(common::FILE_NAME_SEPARATOR); + case MOVE_TO_RECYCLE_BIN: + if (fileExists(fileObj.getFullName<side>())) + { + const Zstring targetFile = getSessionDir<side>() + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator + const Zstring targetDir = targetFile.BeforeLast(common::FILE_NAME_SEPARATOR); - if (!dirExists(targetDir)) - createDirectory(targetDir); //throw (FileError) + if (!dirExists(targetDir)) + createDirectory(targetDir); //throw (FileError) - try //rename file: no copying!!! - { - //performance optimization!! Instead of moving each object into recycle bin separately, we rename them ony by one into a - //temporary directory and delete this directory only ONCE! - renameFile(fileObj.getFullName<side>(), targetFile); //throw (FileError); - } - catch (...) - { - //if anything went wrong, move to recycle bin the standard way (single file processing: slow) - moveToRecycleBin(fileObj.getFullName<side>()); //throw (FileError) + try //rename file: no copying!!! + { + //performance optimization!! Instead of moving each object into recycle bin separately, we rename them ony by one into a + //temporary directory and delete this directory only ONCE! + renameFile(fileObj.getFullName<side>(), targetFile); //throw (FileError); + } + catch (...) + { + //if anything went wrong, move to recycle bin the standard way (single file processing: slow) + moveToRecycleBin(fileObj.getFullName<side>()); //throw (FileError) + } } - } - break; + break; - case MOVE_TO_CUSTOM_DIRECTORY: - if (fileExists(fileObj.getFullName<side>())) - { - const Zstring targetFile = getSessionDir<side>() + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator - const Zstring targetDir = targetFile.BeforeLast(common::FILE_NAME_SEPARATOR); + case MOVE_TO_CUSTOM_DIRECTORY: + if (fileExists(fileObj.getFullName<side>())) + { + const Zstring targetFile = getSessionDir<side>() + fileObj.getRelativeName<side>(); //altDeletionDir ends with path separator + const Zstring targetDir = targetFile.BeforeLast(common::FILE_NAME_SEPARATOR); - if (!dirExists(targetDir)) - createDirectory(targetDir); //throw (FileError) + if (!dirExists(targetDir)) + createDirectory(targetDir); //throw (FileError) - MoveFileCallbackImpl callBack(statusUpdater_); //if file needs to be copied we need callback functionality to update screen and offer abort - moveFile(fileObj.getFullName<side>(), targetFile, &callBack); - } - break; + MoveFileCallbackImpl callBack(statusUpdater_); //if file needs to be copied we need callback functionality to update screen and offer abort + moveFile(fileObj.getFullName<side>(), targetFile, &callBack); + } + break; } } @@ -790,50 +785,50 @@ void DeletionHandling::removeFolder(const FileSystemObject& dirObj) const switch (deletionType) { - case DELETE_PERMANENTLY: - { - RemoveDirCallbackImpl remDirCallback(statusUpdater_); - removeDirectory(dirObj.getFullName<side>(), &remDirCallback); - } - break; - - case MOVE_TO_RECYCLE_BIN: - if (dirExists(dirObj.getFullName<side>())) + case DELETE_PERMANENTLY: { - const Zstring targetDir = getSessionDir<side>() + dirObj.getRelativeName<side>(); - const Zstring targetSuperDir = targetDir.BeforeLast(common::FILE_NAME_SEPARATOR); - - if (!dirExists(targetSuperDir)) - createDirectory(targetSuperDir); //throw (FileError) + RemoveDirCallbackImpl remDirCallback(statusUpdater_); + removeDirectory(dirObj.getFullName<side>(), &remDirCallback); + } + break; - try //rename directory: no copying!!! - { - //performance optimization!! Instead of moving each object into recycle bin separately, we rename them ony by one into a - //temporary directory and delete this directory only ONCE! - renameFile(dirObj.getFullName<side>(), targetDir); //throw (FileError); - } - catch (...) + case MOVE_TO_RECYCLE_BIN: + if (dirExists(dirObj.getFullName<side>())) { - //if anything went wrong, move to recycle bin the standard way (single file processing: slow) - moveToRecycleBin(dirObj.getFullName<side>()); //throw (FileError) + const Zstring targetDir = getSessionDir<side>() + dirObj.getRelativeName<side>(); + const Zstring targetSuperDir = targetDir.BeforeLast(common::FILE_NAME_SEPARATOR); + + if (!dirExists(targetSuperDir)) + createDirectory(targetSuperDir); //throw (FileError) + try //rename directory: no copying!!! + { + //performance optimization!! Instead of moving each object into recycle bin separately, we rename them ony by one into a + //temporary directory and delete this directory only ONCE! + renameFile(dirObj.getFullName<side>(), targetDir); //throw (FileError); + } + catch (...) + { + //if anything went wrong, move to recycle bin the standard way (single file processing: slow) + moveToRecycleBin(dirObj.getFullName<side>()); //throw (FileError) + + } } - } - break; + break; - case MOVE_TO_CUSTOM_DIRECTORY: - if (dirExists(dirObj.getFullName<side>())) - { - const Zstring targetDir = getSessionDir<side>() + dirObj.getRelativeName<side>(); - const Zstring targetSuperDir = targetDir.BeforeLast(common::FILE_NAME_SEPARATOR); + case MOVE_TO_CUSTOM_DIRECTORY: + if (dirExists(dirObj.getFullName<side>())) + { + const Zstring targetDir = getSessionDir<side>() + dirObj.getRelativeName<side>(); + const Zstring targetSuperDir = targetDir.BeforeLast(common::FILE_NAME_SEPARATOR); - if (!dirExists(targetSuperDir)) - createDirectory(targetSuperDir); //throw (FileError) + if (!dirExists(targetSuperDir)) + createDirectory(targetSuperDir); //throw (FileError) - MoveFileCallbackImpl callBack(statusUpdater_); //if files need to be copied, we need callback functionality to update screen and offer abort - moveDirectory(dirObj.getFullName<side>(), targetDir, true, &callBack); - } - break; + MoveFileCallbackImpl callBack(statusUpdater_); //if files need to be copied, we need callback functionality to update screen and offer abort + moveDirectory(dirObj.getFullName<side>(), targetDir, true, &callBack); + } + break; } } //---------------------------------------------------------------------------------------- @@ -845,24 +840,24 @@ bool diskSpaceIsReduced(const FileMapping& fileObj) { switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction { - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - return true; - - case SO_OVERWRITE_LEFT: - return fileObj.getFileSize<LEFT_SIDE>() > fileObj.getFileSize<RIGHT_SIDE>(); - - case SO_OVERWRITE_RIGHT: - return fileObj.getFileSize<LEFT_SIDE>() < fileObj.getFileSize<RIGHT_SIDE>(); - - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - return false; + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + return true; + + case SO_OVERWRITE_LEFT: + return fileObj.getFileSize<LEFT_SIDE>() > fileObj.getFileSize<RIGHT_SIDE>(); + + case SO_OVERWRITE_RIGHT: + return fileObj.getFileSize<LEFT_SIDE>() < fileObj.getFileSize<RIGHT_SIDE>(); + + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + return false; } return false; //dummy } @@ -872,21 +867,21 @@ bool diskSpaceIsReduced(const DirMapping& dirObj) { switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction { - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - return true; - - case SO_OVERWRITE_LEFT: - case SO_OVERWRITE_RIGHT: - assert(false); - case SO_UNRESOLVED_CONFLICT: - case SO_CREATE_NEW_LEFT: - case SO_CREATE_NEW_RIGHT: - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_COPY_METADATA_TO_LEFT: - case SO_COPY_METADATA_TO_RIGHT: - return false; + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + return true; + + case SO_OVERWRITE_LEFT: + case SO_OVERWRITE_RIGHT: + assert(false); + case SO_UNRESOLVED_CONFLICT: + case SO_CREATE_NEW_LEFT: + case SO_CREATE_NEW_RIGHT: + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_COPY_METADATA_TO_LEFT: + case SO_COPY_METADATA_TO_RIGHT: + return false; } return false; //dummy } @@ -964,8 +959,8 @@ void SynchronizeFolderPair::execute(HierarchyObject& hierObj) //synchronize files: for (HierarchyObject::SubFileMapping::iterator i = hierObj.useSubFiles().begin(); i != hierObj.useSubFiles().end(); ++i) { - if ( ( reduceDiskSpace && diskSpaceIsReduced(*i)) || - (!reduceDiskSpace && !diskSpaceIsReduced(*i))) + if (( reduceDiskSpace && diskSpaceIsReduced(*i)) || + (!reduceDiskSpace && !diskSpaceIsReduced(*i))) tryReportingError(statusUpdater_, boost::bind(&SynchronizeFolderPair::synchronizeFile, this, boost::ref(*i))); } @@ -979,8 +974,8 @@ void SynchronizeFolderPair::execute(HierarchyObject& hierObj) { const SyncOperation syncOp = i->getSyncOperation(); - if ( ( reduceDiskSpace && diskSpaceIsReduced(*i)) || //ensure folder creation happens in second pass, to enable time adaption below - (!reduceDiskSpace && !diskSpaceIsReduced(*i))) // + if (( reduceDiskSpace && diskSpaceIsReduced(*i)) || //ensure folder creation happens in second pass, to enable time adaption below + (!reduceDiskSpace && !diskSpaceIsReduced(*i))) // tryReportingError(statusUpdater_, boost::bind(&SynchronizeFolderPair::synchronizeFolder, this, boost::ref(*i))); //recursive synchronization: @@ -991,29 +986,40 @@ void SynchronizeFolderPair::execute(HierarchyObject& hierObj) { switch (syncOp) { - case SO_CREATE_NEW_LEFT: - case SO_COPY_METADATA_TO_LEFT: - copyFileTimes(i->getFullName<RIGHT_SIDE>(), i->getFullName<LEFT_SIDE>(), true); //deref symlinks; throw (FileError) - break; - case SO_CREATE_NEW_RIGHT: - case SO_COPY_METADATA_TO_RIGHT: - copyFileTimes(i->getFullName<LEFT_SIDE>(), i->getFullName<RIGHT_SIDE>(), true); //deref symlinks; throw (FileError) - break; - case SO_OVERWRITE_RIGHT: - case SO_OVERWRITE_LEFT: - assert(false); - case SO_UNRESOLVED_CONFLICT: - case SO_DELETE_LEFT: - case SO_DELETE_RIGHT: - case SO_DO_NOTHING: - case SO_EQUAL: - break; + case SO_CREATE_NEW_LEFT: + case SO_COPY_METADATA_TO_LEFT: + copyFileTimes(i->getFullName<RIGHT_SIDE>(), i->getFullName<LEFT_SIDE>(), true); //deref symlinks; throw (FileError) + break; + case SO_CREATE_NEW_RIGHT: + case SO_COPY_METADATA_TO_RIGHT: + copyFileTimes(i->getFullName<LEFT_SIDE>(), i->getFullName<RIGHT_SIDE>(), true); //deref symlinks; throw (FileError) + break; + case SO_OVERWRITE_RIGHT: + case SO_OVERWRITE_LEFT: + assert(false); + case SO_UNRESOLVED_CONFLICT: + case SO_DELETE_LEFT: + case SO_DELETE_RIGHT: + case SO_DO_NOTHING: + case SO_EQUAL: + break; } } catch (...) {} } } +namespace +{ +//runtime impact per file: SSD: 0s, HDD: 43 µs, USB stick: 1 ms +inline +void checkFileReadable(const Zstring& filename) //throw (FileError) +{ + ffs3::FileInput file(filename); //throw (FileError) + char buffer[1]; + file.read(buffer, 1); // +} +} void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const { @@ -1022,110 +1028,118 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const switch (fileObj.getSyncOperation()) //evaluate comparison result and sync direction { - case SO_CREATE_NEW_LEFT: - target = fileObj.getBaseDirPf<LEFT_SIDE>() + fileObj.getRelativeName<RIGHT_SIDE>(); //can't use "getFullName" as target is not yet existing + case SO_CREATE_NEW_LEFT: + target = fileObj.getBaseDirPf<LEFT_SIDE>() + fileObj.getRelativeName<RIGHT_SIDE>(); //can't use "getFullName" as target is not yet existing - statusText = txtCopyingFile; - statusText.Replace(Zstr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false); - statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + statusText = txtCopyingFile; + statusText.Replace(Zstr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false); + statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target, fileObj.getFileSize<RIGHT_SIDE>()); - break; + copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target, fileObj.getFileSize<RIGHT_SIDE>()); + break; - case SO_CREATE_NEW_RIGHT: - target = fileObj.getBaseDirPf<RIGHT_SIDE>() + fileObj.getRelativeName<LEFT_SIDE>(); + case SO_CREATE_NEW_RIGHT: + target = fileObj.getBaseDirPf<RIGHT_SIDE>() + fileObj.getRelativeName<LEFT_SIDE>(); - statusText = txtCopyingFile; - statusText.Replace(Zstr("%x"), fileObj.getShortName<LEFT_SIDE>(), false); - statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + statusText = txtCopyingFile; + statusText.Replace(Zstr("%x"), fileObj.getShortName<LEFT_SIDE>(), false); + statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target, fileObj.getFileSize<LEFT_SIDE>()); - break; + copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target, fileObj.getFileSize<LEFT_SIDE>()); + break; - case SO_DELETE_LEFT: - statusText = delHandling_.getTxtRemovingFile(); - statusText.Replace(Zstr("%x"), fileObj.getFullName<LEFT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_DELETE_LEFT: + statusText = delHandling_.getTxtRemovingFile(); + statusText.Replace(Zstr("%x"), fileObj.getFullName<LEFT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - delHandling_.removeFile<LEFT_SIDE>(fileObj); //throw FileError() - break; + delHandling_.removeFile<LEFT_SIDE>(fileObj); //throw FileError() + break; - case SO_DELETE_RIGHT: - statusText = delHandling_.getTxtRemovingFile(); - statusText.Replace(Zstr("%x"), fileObj.getFullName<RIGHT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_DELETE_RIGHT: + statusText = delHandling_.getTxtRemovingFile(); + statusText.Replace(Zstr("%x"), fileObj.getFullName<RIGHT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - delHandling_.removeFile<RIGHT_SIDE>(fileObj); //throw FileError() - break; + delHandling_.removeFile<RIGHT_SIDE>(fileObj); //throw FileError() + break; - case SO_OVERWRITE_LEFT: - target = fileObj.getBaseDirPf<LEFT_SIDE>() + fileObj.getRelativeName<RIGHT_SIDE>(); //respect differences in case of source object + case SO_OVERWRITE_LEFT: + target = fileObj.getBaseDirPf<LEFT_SIDE>() + fileObj.getRelativeName<RIGHT_SIDE>(); //respect differences in case of source object - statusText = txtOverwritingFile; - statusText.Replace(Zstr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false); - statusText.Replace(Zstr("%y"), fileObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + statusText = txtOverwritingFile; + statusText.Replace(Zstr("%x"), fileObj.getShortName<RIGHT_SIDE>(), false); + statusText.Replace(Zstr("%y"), fileObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - delHandling_.removeFile<LEFT_SIDE>(fileObj); //throw FileError() - fileObj.removeObject<LEFT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) + //1. check read access: don't delete target file if source cannot be read (e.g. is locked) + checkFileReadable(fileObj.getFullName<RIGHT_SIDE>()); //throw (FileError) - copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target, fileObj.getFileSize<RIGHT_SIDE>()); - break; + //2. delete target and copy source + delHandling_.removeFile<LEFT_SIDE>(fileObj); //throw FileError() + fileObj.removeObject<LEFT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) - case SO_OVERWRITE_RIGHT: - target = fileObj.getBaseDirPf<RIGHT_SIDE>() + fileObj.getRelativeName<LEFT_SIDE>(); //respect differences in case of source object + copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target, fileObj.getFileSize<RIGHT_SIDE>()); + break; - statusText = txtOverwritingFile; - statusText.Replace(Zstr("%x"), fileObj.getShortName<LEFT_SIDE>(), false); - statusText.Replace(Zstr("%y"), fileObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_OVERWRITE_RIGHT: + target = fileObj.getBaseDirPf<RIGHT_SIDE>() + fileObj.getRelativeName<LEFT_SIDE>(); //respect differences in case of source object - delHandling_.removeFile<RIGHT_SIDE>(fileObj); //throw FileError() - fileObj.removeObject<RIGHT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) + statusText = txtOverwritingFile; + statusText.Replace(Zstr("%x"), fileObj.getShortName<LEFT_SIDE>(), false); + statusText.Replace(Zstr("%y"), fileObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target, fileObj.getFileSize<LEFT_SIDE>()); - break; + //1. check read access: don't delete target file if source cannot be read (e.g. is locked) + checkFileReadable(fileObj.getFullName<LEFT_SIDE>()); //throw (FileError) - case SO_COPY_METADATA_TO_LEFT: - statusText = txtWritingAttributes; - statusText.Replace(Zstr("%x"), fileObj.getFullName<LEFT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + //2. delete target and copy source + delHandling_.removeFile<RIGHT_SIDE>(fileObj); //throw FileError() + fileObj.removeObject<RIGHT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) - if (fileObj.getShortName<LEFT_SIDE>() != fileObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) - moveFile(fileObj.getFullName<LEFT_SIDE>(), - fileObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + fileObj.getShortName<RIGHT_SIDE>()); //throw (FileError); + copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target, fileObj.getFileSize<LEFT_SIDE>()); + break; - if (!sameFileTime(fileObj.getLastWriteTime<LEFT_SIDE>(), fileObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision - copyFileTimes(fileObj.getFullName<RIGHT_SIDE>(), fileObj.getFullName<LEFT_SIDE>(), true); //deref symlinks; throw (FileError) - break; + case SO_COPY_METADATA_TO_LEFT: + statusText = txtWritingAttributes; + statusText.Replace(Zstr("%x"), fileObj.getFullName<LEFT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh + + if (fileObj.getShortName<LEFT_SIDE>() != fileObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) + moveFile(fileObj.getFullName<LEFT_SIDE>(), + fileObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + fileObj.getShortName<RIGHT_SIDE>()); //throw (FileError); - case SO_COPY_METADATA_TO_RIGHT: - statusText = txtWritingAttributes; - statusText.Replace(Zstr("%x"), fileObj.getFullName<RIGHT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + if (!sameFileTime(fileObj.getLastWriteTime<LEFT_SIDE>(), fileObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision + copyFileTimes(fileObj.getFullName<RIGHT_SIDE>(), fileObj.getFullName<LEFT_SIDE>(), true); //deref symlinks; throw (FileError) + break; - if (fileObj.getShortName<LEFT_SIDE>() != fileObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) - moveFile(fileObj.getFullName<RIGHT_SIDE>(), - fileObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + fileObj.getShortName<LEFT_SIDE>()); //throw (FileError); + case SO_COPY_METADATA_TO_RIGHT: + statusText = txtWritingAttributes; + statusText.Replace(Zstr("%x"), fileObj.getFullName<RIGHT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - if (!sameFileTime(fileObj.getLastWriteTime<LEFT_SIDE>(), fileObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision - copyFileTimes(fileObj.getFullName<LEFT_SIDE>(), fileObj.getFullName<RIGHT_SIDE>(), true); //deref symlinks; throw (FileError) - break; + if (fileObj.getShortName<LEFT_SIDE>() != fileObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) + moveFile(fileObj.getFullName<RIGHT_SIDE>(), + fileObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + fileObj.getShortName<LEFT_SIDE>()); //throw (FileError); + + if (!sameFileTime(fileObj.getLastWriteTime<LEFT_SIDE>(), fileObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision + copyFileTimes(fileObj.getFullName<LEFT_SIDE>(), fileObj.getFullName<RIGHT_SIDE>(), true); //deref symlinks; throw (FileError) + break; - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - return; //no update on processed data! + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + return; //no update on processed data! } //update FileMapping @@ -1144,110 +1158,110 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const switch (linkObj.getSyncOperation()) //evaluate comparison result and sync direction { - case SO_CREATE_NEW_LEFT: - target = linkObj.getBaseDirPf<LEFT_SIDE>() + linkObj.getRelativeName<RIGHT_SIDE>(); + case SO_CREATE_NEW_LEFT: + target = linkObj.getBaseDirPf<LEFT_SIDE>() + linkObj.getRelativeName<RIGHT_SIDE>(); - statusText = txtCopyingLink; - statusText.Replace(Zstr("%x"), linkObj.getShortName<RIGHT_SIDE>(), false); - statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + statusText = txtCopyingLink; + statusText.Replace(Zstr("%x"), linkObj.getShortName<RIGHT_SIDE>(), false); + statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - copySymlink(linkObj.getFullName<RIGHT_SIDE>(), target, linkObj.getLinkType<RIGHT_SIDE>()); - break; + copySymlink(linkObj.getFullName<RIGHT_SIDE>(), target, linkObj.getLinkType<RIGHT_SIDE>()); + break; - case SO_CREATE_NEW_RIGHT: - target = linkObj.getBaseDirPf<RIGHT_SIDE>() + linkObj.getRelativeName<LEFT_SIDE>(); + case SO_CREATE_NEW_RIGHT: + target = linkObj.getBaseDirPf<RIGHT_SIDE>() + linkObj.getRelativeName<LEFT_SIDE>(); - statusText = txtCopyingLink; - statusText.Replace(Zstr("%x"), linkObj.getShortName<LEFT_SIDE>(), false); - statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + statusText = txtCopyingLink; + statusText.Replace(Zstr("%x"), linkObj.getShortName<LEFT_SIDE>(), false); + statusText.Replace(Zstr("%y"), target.BeforeLast(common::FILE_NAME_SEPARATOR), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - copySymlink(linkObj.getFullName<LEFT_SIDE>(), target, linkObj.getLinkType<LEFT_SIDE>()); - break; + copySymlink(linkObj.getFullName<LEFT_SIDE>(), target, linkObj.getLinkType<LEFT_SIDE>()); + break; - case SO_DELETE_LEFT: - statusText = delHandling_.getTxtRemovingSymLink(); - statusText.Replace(Zstr("%x"), linkObj.getFullName<LEFT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_DELETE_LEFT: + statusText = delHandling_.getTxtRemovingSymLink(); + statusText.Replace(Zstr("%x"), linkObj.getFullName<LEFT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - deleteSymlink<LEFT_SIDE>(linkObj); //throw FileError() - break; + deleteSymlink<LEFT_SIDE>(linkObj); //throw FileError() + break; - case SO_DELETE_RIGHT: - statusText = delHandling_.getTxtRemovingSymLink(); - statusText.Replace(Zstr("%x"), linkObj.getFullName<RIGHT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_DELETE_RIGHT: + statusText = delHandling_.getTxtRemovingSymLink(); + statusText.Replace(Zstr("%x"), linkObj.getFullName<RIGHT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - deleteSymlink<RIGHT_SIDE>(linkObj); //throw FileError() - break; + deleteSymlink<RIGHT_SIDE>(linkObj); //throw FileError() + break; - case SO_OVERWRITE_LEFT: - target = linkObj.getBaseDirPf<LEFT_SIDE>() + linkObj.getRelativeName<RIGHT_SIDE>(); //respect differences in case of source object + case SO_OVERWRITE_LEFT: + target = linkObj.getBaseDirPf<LEFT_SIDE>() + linkObj.getRelativeName<RIGHT_SIDE>(); //respect differences in case of source object - statusText = txtOverwritingLink; - statusText.Replace(Zstr("%x"), linkObj.getShortName<RIGHT_SIDE>(), false); - statusText.Replace(Zstr("%y"), linkObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + statusText = txtOverwritingLink; + statusText.Replace(Zstr("%x"), linkObj.getShortName<RIGHT_SIDE>(), false); + statusText.Replace(Zstr("%y"), linkObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - deleteSymlink<LEFT_SIDE>(linkObj); //throw FileError() - linkObj.removeObject<LEFT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) + deleteSymlink<LEFT_SIDE>(linkObj); //throw FileError() + linkObj.removeObject<LEFT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) - copySymlink(linkObj.getFullName<RIGHT_SIDE>(), target, linkObj.getLinkType<RIGHT_SIDE>()); - break; + copySymlink(linkObj.getFullName<RIGHT_SIDE>(), target, linkObj.getLinkType<RIGHT_SIDE>()); + break; - case SO_OVERWRITE_RIGHT: - target = linkObj.getBaseDirPf<RIGHT_SIDE>() + linkObj.getRelativeName<LEFT_SIDE>(); //respect differences in case of source object + case SO_OVERWRITE_RIGHT: + target = linkObj.getBaseDirPf<RIGHT_SIDE>() + linkObj.getRelativeName<LEFT_SIDE>(); //respect differences in case of source object - statusText = txtOverwritingLink; - statusText.Replace(Zstr("%x"), linkObj.getShortName<LEFT_SIDE>(), false); - statusText.Replace(Zstr("%y"), linkObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + statusText = txtOverwritingLink; + statusText.Replace(Zstr("%x"), linkObj.getShortName<LEFT_SIDE>(), false); + statusText.Replace(Zstr("%y"), linkObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - deleteSymlink<RIGHT_SIDE>(linkObj); //throw FileError() - linkObj.removeObject<RIGHT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) + deleteSymlink<RIGHT_SIDE>(linkObj); //throw FileError() + linkObj.removeObject<RIGHT_SIDE>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!) - copySymlink(linkObj.getFullName<LEFT_SIDE>(), target, linkObj.getLinkType<LEFT_SIDE>()); - break; + copySymlink(linkObj.getFullName<LEFT_SIDE>(), target, linkObj.getLinkType<LEFT_SIDE>()); + break; - case SO_COPY_METADATA_TO_LEFT: - statusText = txtWritingAttributes; - statusText.Replace(Zstr("%x"), linkObj.getFullName<LEFT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_COPY_METADATA_TO_LEFT: + statusText = txtWritingAttributes; + statusText.Replace(Zstr("%x"), linkObj.getFullName<LEFT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - if (linkObj.getShortName<LEFT_SIDE>() != linkObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) - moveFile(linkObj.getFullName<LEFT_SIDE>(), - linkObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + linkObj.getShortName<RIGHT_SIDE>()); //throw (FileError); + if (linkObj.getShortName<LEFT_SIDE>() != linkObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) + moveFile(linkObj.getFullName<LEFT_SIDE>(), + linkObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + linkObj.getShortName<RIGHT_SIDE>()); //throw (FileError); - if (!sameFileTime(linkObj.getLastWriteTime<LEFT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision - copyFileTimes(linkObj.getFullName<RIGHT_SIDE>(), linkObj.getFullName<LEFT_SIDE>(), false); //don't deref symlinks; throw (FileError) - break; + if (!sameFileTime(linkObj.getLastWriteTime<LEFT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision + copyFileTimes(linkObj.getFullName<RIGHT_SIDE>(), linkObj.getFullName<LEFT_SIDE>(), false); //don't deref symlinks; throw (FileError) + break; - case SO_COPY_METADATA_TO_RIGHT: - statusText = txtWritingAttributes; - statusText.Replace(Zstr("%x"), linkObj.getFullName<RIGHT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_COPY_METADATA_TO_RIGHT: + statusText = txtWritingAttributes; + statusText.Replace(Zstr("%x"), linkObj.getFullName<RIGHT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - if (linkObj.getShortName<LEFT_SIDE>() != linkObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) - moveFile(linkObj.getFullName<RIGHT_SIDE>(), - linkObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + linkObj.getShortName<LEFT_SIDE>()); //throw (FileError); + if (linkObj.getShortName<LEFT_SIDE>() != linkObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) + moveFile(linkObj.getFullName<RIGHT_SIDE>(), + linkObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + linkObj.getShortName<LEFT_SIDE>()); //throw (FileError); - if (!sameFileTime(linkObj.getLastWriteTime<LEFT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision - copyFileTimes(linkObj.getFullName<LEFT_SIDE>(), linkObj.getFullName<RIGHT_SIDE>(), false); //don't deref symlinks; throw (FileError) - break; + if (!sameFileTime(linkObj.getLastWriteTime<LEFT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>(), 2)) ////respect 2 second FAT/FAT32 precision + copyFileTimes(linkObj.getFullName<LEFT_SIDE>(), linkObj.getFullName<RIGHT_SIDE>(), false); //don't deref symlinks; throw (FileError) + break; - case SO_DO_NOTHING: - case SO_EQUAL: - case SO_UNRESOLVED_CONFLICT: - return; //no update on processed data! + case SO_DO_NOTHING: + case SO_EQUAL: + case SO_UNRESOLVED_CONFLICT: + return; //no update on processed data! } //update FileMapping @@ -1267,103 +1281,103 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const //synchronize folders: switch (dirObj.getSyncOperation()) //evaluate comparison result and sync direction { - case SO_CREATE_NEW_LEFT: - target = dirObj.getBaseDirPf<LEFT_SIDE>() + dirObj.getRelativeName<RIGHT_SIDE>(); - - statusText = txtCreatingFolder; - statusText.Replace(Zstr("%x"), target, false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh - - //some check to catch the error that directory on source has been deleted externally after "compare"... - if (!ffs3::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>(), false, copyFilePermissions_); //no symlink copying! - break; + case SO_CREATE_NEW_LEFT: + target = dirObj.getBaseDirPf<LEFT_SIDE>() + dirObj.getRelativeName<RIGHT_SIDE>(); + + statusText = txtCreatingFolder; + statusText.Replace(Zstr("%x"), target, false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh + + //some check to catch the error that directory on source has been deleted externally after "compare"... + if (!ffs3::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>(), false, copyFilePermissions_); //no symlink copying! + break; - case SO_CREATE_NEW_RIGHT: - target = dirObj.getBaseDirPf<RIGHT_SIDE>() + dirObj.getRelativeName<LEFT_SIDE>(); + case SO_CREATE_NEW_RIGHT: + target = dirObj.getBaseDirPf<RIGHT_SIDE>() + dirObj.getRelativeName<LEFT_SIDE>(); - statusText = txtCreatingFolder; - statusText.Replace(Zstr("%x"), target, false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + statusText = txtCreatingFolder; + statusText.Replace(Zstr("%x"), target, false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - //some check to catch the error that directory on source has been deleted externally after "compare"... - if (!ffs3::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>(), false, copyFilePermissions_); //no symlink copying! - break; + //some check to catch the error that directory on source has been deleted externally after "compare"... + if (!ffs3::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>(), false, copyFilePermissions_); //no symlink copying! + break; - case SO_DELETE_LEFT: - //status information - statusText = delHandling_.getTxtRemovingDir(); - statusText.Replace(Zstr("%x"), dirObj.getFullName<LEFT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_DELETE_LEFT: + //status information + statusText = delHandling_.getTxtRemovingDir(); + statusText.Replace(Zstr("%x"), dirObj.getFullName<LEFT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - delHandling_.removeFolder<LEFT_SIDE>(dirObj); //throw FileError() - { - //progress indicator update: DON'T forget to notify about implicitly deleted objects! - const SyncStatistics subObjects(dirObj); - //...then remove everything - dirObj.useSubFiles().clear(); - dirObj.useSubLinks().clear(); - dirObj.useSubDirs().clear(); - statusUpdater_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble()); - } - break; + delHandling_.removeFolder<LEFT_SIDE>(dirObj); //throw FileError() + { + //progress indicator update: DON'T forget to notify about implicitly deleted objects! + const SyncStatistics subObjects(dirObj); + //...then remove everything + dirObj.useSubFiles().clear(); + dirObj.useSubLinks().clear(); + dirObj.useSubDirs().clear(); + statusUpdater_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble()); + } + break; - case SO_DELETE_RIGHT: - //status information - statusText = delHandling_.getTxtRemovingDir(); - statusText.Replace(Zstr("%x"), dirObj.getFullName<RIGHT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_DELETE_RIGHT: + //status information + statusText = delHandling_.getTxtRemovingDir(); + statusText.Replace(Zstr("%x"), dirObj.getFullName<RIGHT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - delHandling_.removeFolder<RIGHT_SIDE>(dirObj); //throw FileError() - { - //progress indicator update: DON'T forget to notify about implicitly deleted objects! - const SyncStatistics subObjects(dirObj); - //...then remove everything - dirObj.useSubFiles().clear(); - dirObj.useSubLinks().clear(); - dirObj.useSubDirs().clear(); - statusUpdater_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble()); - } - break; + delHandling_.removeFolder<RIGHT_SIDE>(dirObj); //throw FileError() + { + //progress indicator update: DON'T forget to notify about implicitly deleted objects! + const SyncStatistics subObjects(dirObj); + //...then remove everything + dirObj.useSubFiles().clear(); + dirObj.useSubLinks().clear(); + dirObj.useSubDirs().clear(); + statusUpdater_.updateProcessedData(subObjects.getCreate() + subObjects.getOverwrite() + subObjects.getDelete(), subObjects.getDataToProcess().ToDouble()); + } + break; - case SO_COPY_METADATA_TO_LEFT: - statusText = txtWritingAttributes; - statusText.Replace(Zstr("%x"), dirObj.getFullName<LEFT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_COPY_METADATA_TO_LEFT: + statusText = txtWritingAttributes; + statusText.Replace(Zstr("%x"), dirObj.getFullName<LEFT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - if (dirObj.getShortName<LEFT_SIDE>() != dirObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) - moveFile(dirObj.getFullName<LEFT_SIDE>(), - dirObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + dirObj.getShortName<RIGHT_SIDE>()); //throw (FileError); - //copyFileTimes(dirObj.getFullName<RIGHT_SIDE>(), dirObj.getFullName<LEFT_SIDE>(), true); //throw (FileError) -> is executed after sub-objects have finished synchronization - break; + if (dirObj.getShortName<LEFT_SIDE>() != dirObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) + moveFile(dirObj.getFullName<LEFT_SIDE>(), + dirObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + dirObj.getShortName<RIGHT_SIDE>()); //throw (FileError); + //copyFileTimes(dirObj.getFullName<RIGHT_SIDE>(), dirObj.getFullName<LEFT_SIDE>(), true); //throw (FileError) -> is executed after sub-objects have finished synchronization + break; - case SO_COPY_METADATA_TO_RIGHT: - statusText = txtWritingAttributes; - statusText.Replace(Zstr("%x"), dirObj.getFullName<RIGHT_SIDE>(), false); - statusUpdater_.reportInfo(statusText); - statusUpdater_.requestUiRefresh(); //trigger display refresh + case SO_COPY_METADATA_TO_RIGHT: + statusText = txtWritingAttributes; + statusText.Replace(Zstr("%x"), dirObj.getFullName<RIGHT_SIDE>(), false); + statusUpdater_.reportInfo(statusText); + statusUpdater_.requestUiRefresh(); //trigger display refresh - if (dirObj.getShortName<LEFT_SIDE>() != dirObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) - moveFile(dirObj.getFullName<RIGHT_SIDE>(), - dirObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + dirObj.getShortName<LEFT_SIDE>()); //throw (FileError); - //copyFileTimes(dirObj.getFullName<LEFT_SIDE>(), dirObj.getFullName<RIGHT_SIDE>(), true); //throw (FileError) -> is executed after sub-objects have finished synchronization - break; + if (dirObj.getShortName<LEFT_SIDE>() != dirObj.getShortName<RIGHT_SIDE>()) //adapt difference in case (windows only) + moveFile(dirObj.getFullName<RIGHT_SIDE>(), + dirObj.getFullName<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + dirObj.getShortName<LEFT_SIDE>()); //throw (FileError); + //copyFileTimes(dirObj.getFullName<LEFT_SIDE>(), dirObj.getFullName<RIGHT_SIDE>(), true); //throw (FileError) -> is executed after sub-objects have finished synchronization + break; - case SO_OVERWRITE_RIGHT: - case SO_OVERWRITE_LEFT: - assert(false); - case SO_UNRESOLVED_CONFLICT: - case SO_DO_NOTHING: - case SO_EQUAL: - return; //no update on processed data! + case SO_OVERWRITE_RIGHT: + case SO_OVERWRITE_LEFT: + assert(false); + case SO_UNRESOLVED_CONFLICT: + case SO_DO_NOTHING: + case SO_EQUAL: + return; //no update on processed data! } //update DirMapping @@ -1421,6 +1435,9 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf //PERF_START; + if (syncConfig.size() != folderCmp.size()) + throw std::logic_error("Programming Error: Contract violation!"); + //inform about the total amount of data that will be processed from now on const SyncStatistics statisticsTotal(folderCmp); @@ -1430,10 +1447,14 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf common::convertToSigned(statisticsTotal.getDataToProcess()), StatusHandler::PROCESS_SYNCHRONIZING); + if (!synchronizationNeeded(statisticsTotal)) + statusUpdater.reportInfo(wxToZ(_("Nothing to synchronize according to configuration!"))); //inform about this special case + + + std::deque<bool> skipFolderPair(folderCmp.size()); //folder pairs may be skipped after fatal errors were found + //-------------------some basic checks:------------------------------------------ - if (syncConfig.size() != folderCmp.size()) - throw std::logic_error("Programming Error: Contract violation!"); //aggregate information typedef std::set<Zstring, LessDependentDirectory> DirReadSet; //count (at least one) read access @@ -1451,14 +1472,17 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf //start checking folder pairs for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) { + const size_t folderIndex = j - folderCmp.begin(); + //exclude some pathological case (leftdir, rightdir are empty) if (EqualFilename()(j->getBaseDir<LEFT_SIDE>(), j->getBaseDir<RIGHT_SIDE>())) continue; - const FolderPairSyncCfg& folderPairCfg = syncConfig[j - folderCmp.begin()]; + const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; + const SyncStatistics statisticsFolderPair(*j); - //aggregate information of folders used by multiple pairs in read/write access + //aggregate basic information const bool writeLeft = statisticsFolderPair.getCreate <LEFT_SIDE>() + statisticsFolderPair.getOverwrite<LEFT_SIDE>() + statisticsFolderPair.getDelete <LEFT_SIDE>() > 0; @@ -1466,6 +1490,26 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf const bool writeRight = statisticsFolderPair.getCreate <RIGHT_SIDE>() + statisticsFolderPair.getOverwrite<RIGHT_SIDE>() + statisticsFolderPair.getDelete <RIGHT_SIDE>() > 0; + + //skip folder pair if there is nothing to do (except for automatic mode, where data base needs to be written even in this case) + if (!writeLeft && !writeRight && + !folderPairCfg.inAutomaticMode) + { + skipFolderPair[folderIndex] = true; //skip creating (not yet existing) base directories in particular if there's no need + continue; + } + + + //check empty input fields: basically this only makes sense if empty field is not target (and not automatic mode: because of db file creation) + if ((j->getBaseDir<LEFT_SIDE>(). empty() && (writeLeft || folderPairCfg.inAutomaticMode)) || + (j->getBaseDir<RIGHT_SIDE>().empty() && (writeRight || folderPairCfg.inAutomaticMode))) + { + statusUpdater.reportFatalError(_("Cannot write to empty directory path!")); + skipFolderPair[folderIndex] = true; + continue; + } + + //aggregate information of folders used by multiple pairs in read/write access if (!EqualDependentDirectory()(j->getBaseDir<LEFT_SIDE>(), j->getBaseDir<RIGHT_SIDE>())) //true in general { if (writeLeft) @@ -1488,13 +1532,15 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf ++dirWriteCount[j->getBaseDir<LEFT_SIDE>()]; } + if (statisticsFolderPair.getOverwrite() + statisticsFolderPair.getDelete() > 0) { //test existence of Recycle Bin if (folderPairCfg.handleDeletion == ffs3::MOVE_TO_RECYCLE_BIN && !ffs3::recycleBinExists()) { statusUpdater.reportFatalError(_("Recycle Bin not yet supported for this system!")); - return; //should be obsolete! + skipFolderPair[folderIndex] = true; + continue; } if (folderPairCfg.handleDeletion == ffs3::MOVE_TO_CUSTOM_DIRECTORY) @@ -1503,7 +1549,8 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf if (folderPairCfg.custDelFolder.empty()) { statusUpdater.reportFatalError(_("User-defined directory for deletion was not specified!")); - return; //should be obsolete! + skipFolderPair[folderIndex] = true; + continue; } } } @@ -1512,12 +1559,14 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf if (dataLossPossible(j->getBaseDir<LEFT_SIDE>(), statisticsFolderPair)) { statusUpdater.reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"")); - return; //should be obsolete! + skipFolderPair[folderIndex] = true; + continue; } if (dataLossPossible(j->getBaseDir<RIGHT_SIDE>(), statisticsFolderPair)) { statusUpdater.reportFatalError(wxString(_("Source directory does not exist anymore:")) + wxT("\n\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\"") ); - return; //should be obsolete! + skipFolderPair[folderIndex] = true; + continue; } //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted @@ -1531,7 +1580,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf if (wxGetDiskSpace(zToWx(j->getBaseDir<LEFT_SIDE>()), NULL, &freeDiskSpaceLeft)) { if (0 < freeDiskSpaceLeft && //zero disk space is either an error or not: in both cases this warning message is obsolete (WebDav seems to report 0) - freeDiskSpaceLeft < spaceNeeded.first) + freeDiskSpaceLeft < spaceNeeded.first) diskSpaceMissing.push_back(std::make_pair(j->getBaseDir<LEFT_SIDE>(), std::make_pair(spaceNeeded.first, freeDiskSpaceLeft))); } @@ -1540,7 +1589,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf if (wxGetDiskSpace(zToWx(j->getBaseDir<RIGHT_SIDE>()), NULL, &freeDiskSpaceRight)) { if (0 < freeDiskSpaceRight && //zero disk space is either an error or not: in both cases this warning message is obsolete (WebDav seems to report 0) - freeDiskSpaceRight < spaceNeeded.second) + freeDiskSpaceRight < spaceNeeded.second) diskSpaceMissing.push_back(std::make_pair(j->getBaseDir<RIGHT_SIDE>(), std::make_pair(spaceNeeded.second, freeDiskSpaceRight))); } } @@ -1593,7 +1642,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf for (DirSpaceRequAvailList::const_iterator i = diskSpaceMissing.begin(); i != diskSpaceMissing.end(); ++i) warningMessage += wxString(wxT("\n\n")) + wxT("\"") + zToWx(i->first) + wxT("\"\n") + - _("Total required free disk space:") + wxT(" ") + formatFilesizeToShortString(i->second.first) + wxT("\n") + + _("Free disk space required:") + wxT(" ") + formatFilesizeToShortString(i->second.first) + wxT("\n") + _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(i->second.second); statusUpdater.reportWarning(warningMessage, m_warnings.warningNotEnoughDiskSpace); @@ -1603,8 +1652,8 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf //check if folders are used by multiple pairs in read/write access std::vector<Zstring> conflictDirs; for (DirWriteMap::const_iterator i = dirWriteCount.begin(); i != dirWriteCount.end(); ++i) - if ( i->second >= 2 || //multiple write accesses - (i->second == 1 && dirReadCount.find(i->first) != dirReadCount.end())) //read/write access + if (i->second >= 2 || //multiple write accesses + (i->second == 1 && dirReadCount.find(i->first) != dirReadCount.end())) //read/write access conflictDirs.push_back(i->first); if (!conflictDirs.empty()) @@ -1647,13 +1696,18 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf assert(syncConfig.size() == folderCmp.size()); for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) { - const FolderPairSyncCfg& folderPairCfg = syncConfig[j - folderCmp.begin()]; + const size_t folderIndex = j - folderCmp.begin(); + + const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; + + if (skipFolderPair[folderIndex]) //folder pairs may be skipped after fatal errors were found + continue; //exclude some pathological case (leftdir, rightdir are empty) if (EqualFilename()(j->getBaseDir<LEFT_SIDE>(), j->getBaseDir<RIGHT_SIDE>())) continue; -//------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------ //info about folder pair to be processed (useful for logfile) wxString left = wxString(_("Left")) + wxT(": "); wxString right = wxString(_("Right")) + wxT(": "); @@ -1663,19 +1717,23 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf wxT("\t") + left + wxT("\"") + zToWx(j->getBaseDir<LEFT_SIDE>()) + wxT("\"")+ wxT(" \n") + wxT("\t") + right + wxT("\"") + zToWx(j->getBaseDir<RIGHT_SIDE>()) + wxT("\""); statusUpdater.reportInfo(wxToZ(statusTxt)); -//------------------------------------------------------------------------------------------ - //(try to) create base dir first (if not yet existing) -> no symlink or attribute copying! - try + + //------------------------------------------------------------------------------------------ + + //create base directories first (if not yet existing) -> no symlink or attribute copying! -> single error message instead of one per file (e.g. unplugged network drive) + const Zstring dirnameLeft = j->getBaseDir<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR); + if (!dirnameLeft.empty() && !ffs3::dirExists(dirnameLeft)) { - ffs3::createDirectory(j->getBaseDir<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR)); + if (!tryReportingError(statusUpdater, boost::bind(ffs3::createDirectory, boost::cref(dirnameLeft)))) //may throw in error-callback! + continue; //skip this folder pair } - catch (...) {} - try //create base dir first -> no symlink or attribute copying! + const Zstring dirnameRight = j->getBaseDir<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR); + if (!dirnameRight.empty() && !ffs3::dirExists(dirnameRight)) { - ffs3::createDirectory(j->getBaseDir<RIGHT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR)); + if (!tryReportingError(statusUpdater, boost::bind(ffs3::createDirectory, boost::cref(dirnameRight)))) //may throw in error-callback! + continue; //skip this folder pair } - catch (...) {} -//------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------ //generate name of alternate deletion directory (unique for session AND folder pair) const DeletionHandling currentDelHandling( @@ -1684,10 +1742,10 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf j->getBaseDir<LEFT_SIDE>(), j->getBaseDir<RIGHT_SIDE>(), statusUpdater); -//------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------ //execute synchronization recursively - //enforce removal of invalid entries (where both sides are empty) + //enforce removal of invalid entries (where element on both sides is empty) struct RemoveInvalid { RemoveInvalid(BaseDirMapping& baseDir) : baseDir_(baseDir) {} @@ -1699,35 +1757,34 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf } dummy1(*j); - SynchronizeFolderPair syncFP( *this, + SynchronizeFolderPair syncFP(*this, #ifdef FFS_WIN - shadowCopyHandler.get(), + shadowCopyHandler.get(), #endif - currentDelHandling); + currentDelHandling); //loop through all files twice; reason: first delete files (or overwrite big ones with smaller ones), then copy rest syncFP.startSync<true>(*j); syncFP.startSync<false>(*j); - //(try to gracefully) cleanup temporary folders (Recycle bin optimization) -> will be done in DeletionHandling anyway... - currentDelHandling.tryCleanup(); + //(try to gracefully) cleanup temporary folders (Recycle bin optimization) -> will be done in ~DeletionHandling anyway... + currentDelHandling.tryCleanup(); //show error dialog if necessary -//------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------ //update synchronization database (automatic sync only) if (folderPairCfg.inAutomaticMode) { statusUpdater.reportInfo(wxToZ(_("Generating database..."))); statusUpdater.forceUiRefresh(); - tryReportingError(statusUpdater, boost::bind(ffs3::saveToDisk, boost::cref(*j))); //these call may throw in error-callback! + tryReportingError(statusUpdater, boost::bind(ffs3::saveToDisk, boost::cref(*j))); //may throw in error-callback! } } } catch (const std::exception& e) { statusUpdater.reportFatalError(wxString::FromUTF8(e.what())); - return; //should be obsolete! } } @@ -1819,18 +1876,18 @@ void SynchronizeFolderPair::copySymlink(const Zstring& source, const Zstring& ta switch (type) { - case LinkDescriptor::TYPE_DIR: - ffs3::createDirectory(target, source, true, copyFilePermissions_); //copy symlink - break; + case LinkDescriptor::TYPE_DIR: + ffs3::createDirectory(target, source, true, copyFilePermissions_); //copy symlink + break; - case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link - ffs3::copyFile(source, target, true, //copy symlink - copyFilePermissions_, + case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link + ffs3::copyFile(source, target, true, //copy symlink + copyFilePermissions_, #ifdef FFS_WIN - shadowCopyHandler_, + shadowCopyHandler_, #endif - NULL); - break; + NULL); + break; } } @@ -1840,13 +1897,13 @@ void SynchronizeFolderPair::deleteSymlink(const SymLinkMapping& linkObj) const { switch (linkObj.getLinkType<side>()) { - case LinkDescriptor::TYPE_DIR: - delHandling_.removeFolder<side>(linkObj); //throw (FileError) - break; + case LinkDescriptor::TYPE_DIR: + delHandling_.removeFolder<side>(linkObj); //throw (FileError) + break; - case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link - delHandling_.removeFile<side>(linkObj); //throw (FileError) - break; + case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link + delHandling_.removeFile<side>(linkObj); //throw (FileError) + break; } } |