summaryrefslogtreecommitdiff
path: root/synchronization.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'synchronization.cpp')
-rw-r--r--synchronization.cpp435
1 files changed, 206 insertions, 229 deletions
diff --git a/synchronization.cpp b/synchronization.cpp
index 73e31e68..076111be 100644
--- a/synchronization.cpp
+++ b/synchronization.cpp
@@ -1,7 +1,7 @@
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved *
+// * Copyright (C) ZenJu (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
#include "synchronization.h"
@@ -11,6 +11,7 @@
#include <wx/file.h> //get rid!?
#include <wx+/format_unit.h>
#include <zen/scope_guard.h>
+#include <zen/process_priority.h>
#include <zen/file_handling.h>
#include <zen/recycler.h>
#include "lib/resolve_path.h"
@@ -20,6 +21,7 @@
#include <zen/file_io.h>
#include <zen/time.h>
#include "lib/status_handler_impl.h"
+#include "lib/versioning.h"
#ifdef FFS_WIN
#include <zen/long_path_prefix.h>
@@ -258,6 +260,7 @@ void SyncStatistics::calcStats(const DirMapping& dirObj)
recurse(dirObj); //since we model logical stats, we recurse, even if deletion variant is "recycler" or "versioning + same volume", which is a single physical operation!
}
+//-----------------------------------------------------------------------------------------------------------
std::vector<zen::FolderPairSyncCfg> zen::extractSyncCfg(const MainConfiguration& mainCfg)
{
@@ -278,9 +281,9 @@ std::vector<zen::FolderPairSyncCfg> zen::extractSyncCfg(const MainConfiguration&
output.push_back(
FolderPairSyncCfg(syncCfg.directionCfg.var == DirectionConfig::AUTOMATIC,
syncCfg.handleDeletion,
- syncCfg.customDeletionDirectory));
+ getFormattedDirectoryName(syncCfg.versioningDirectory),
+ syncCfg.versionCountLimit));
}
-
return output;
}
//------------------------------------------------------------------------------------------------------------
@@ -415,58 +418,17 @@ private:
DeletionType delType;
};
*/
-//#################################################################################################################
-
-FolderPairSyncCfg::FolderPairSyncCfg(bool automaticMode,
- const DeletionPolicy handleDel,
- const Zstring& custDelDir) :
- inAutomaticMode(automaticMode),
- handleDeletion(handleDel),
- custDelFolder(zen::getFormattedDirectoryName(custDelDir)) {}
-//-----------------------------------------------------------------------------------------------------------
-/*
-add some postfix to alternate deletion directory: deletionDirectory\<prefix>2010-06-30 12-59-12\
-*/
-
-Zstring findUnusedName(const Zstring& filename)
-{
- //ensure that session directory does not yet exist (must be unique)
- Zstring output = filename;
- for (int i = 1; zen::somethingExists(output); ++i)
- output = filename + Zchar('_') + numberTo<Zstring>(i);
-
- return output;
-}
-
-SyncProcess::SyncProcess(const std::wstring& jobName,
- const std::wstring& timestamp,
- xmlAccess::OptionalDialogs& warnings,
- bool verifyCopiedFiles,
- bool copyLockedFiles,
- bool copyFilePermissions,
- bool transactionalFileCopy,
- bool runWithBackgroundPriority,
- ProcessCallback& handler) :
- verifyCopiedFiles_(verifyCopiedFiles),
- copyLockedFiles_(copyLockedFiles),
- copyFilePermissions_(copyFilePermissions),
- transactionalFileCopy_(transactionalFileCopy),
- m_warnings(warnings),
- procCallback(handler),
- custDelDirShortname(utfCvrtTo<Zstring>(jobName.empty() ? timestamp : jobName + L" " + timestamp))
-{
- if (runWithBackgroundPriority)
- procBackground.reset(new ScheduleForBackgroundProcessing);
-}
//--------------------------------------------------------------------------------------------------------------
class DeletionHandling //e.g. generate name of alternate deletion directory (unique for session AND folder pair)
{
public:
- DeletionHandling(DeletionPolicy handleDel,
- const Zstring& custDelDir, // final custom deletion directory: custDelDir + \ + subdirShort
- const Zstring& subdirShort, //
+ DeletionHandling(DeletionPolicy handleDel, //nothrow!
+ const Zstring& versioningDir,
+ const TimeComp& timeStamp,
+ int versionCountLimit,
+ size_t folderIndex,
const Zstring& baseDirPf, //with separator postfix
ProcessCallback& procCallback);
~DeletionHandling() { try { tryCleanup(); } catch (...) {} /*make sure this stays non-blocking!*/ } //always (try to) clean up, even if synchronization is aborted!
@@ -474,9 +436,9 @@ public:
//clean-up temporary directory (recycle bin optimization)
void tryCleanup(); //throw FileError -> call this in non-exceptional coding, i.e. somewhere after sync!
- void removeFile (const Zstring& relativeName) const; //throw FileError
- void removeFolder(const Zstring& relativeName) const { removeFolderInt(relativeName, nullptr, nullptr); }; //throw FileError
- void removeFolderUpdateStatistics(const Zstring& relativeName, int objectsExpected, Int64 dataExpected) const { removeFolderInt(relativeName, &objectsExpected, &dataExpected); }; //throw FileError
+ void removeFile (const Zstring& relativeName); //throw FileError
+ void removeFolder(const Zstring& relativeName) { removeFolderInt(relativeName, nullptr, nullptr); }; //throw FileError
+ void removeFolderUpdateStatistics(const Zstring& relativeName, int objectsExpected, Int64 dataExpected) { removeFolderInt(relativeName, &objectsExpected, &dataExpected); }; //throw FileError
//in contrast to "removeFolder()" this function will update statistics!
const std::wstring& getTxtRemovingFile () const { return txtRemovingFile; } //
@@ -490,15 +452,25 @@ public:
#endif
private:
- void removeFolderInt(const Zstring& relativeName, const int* objectsExpected, const Int64* dataExpected) const; //throw FileError
+ DeletionHandling(const DeletionHandling&);
+ DeletionHandling& operator=(const DeletionHandling&);
+ void removeFolderInt(const Zstring& relativeName, const int* objectsExpected, const Int64* dataExpected); //throw FileError
void setDeletionPolicy(DeletionPolicy newPolicy);
- ProcessCallback* const procCallback_; //always bound! need assignment operator => not a reference
+ FileVersioner& getOrCreateVersioner() //throw FileError! => dont create in DeletionHandling()!!!
+ {
+ if (!versioner.get())
+ versioner = make_unique<FileVersioner>(versioningDir_, timeStamp_, versionCountLimit_); //throw FileError
+ return *versioner;
+ };
+
+ ProcessCallback& procCallback_;
const Zstring baseDirPf_; //ends with path separator
- const Zstring custDelDir_;
- const Zstring recyclerTmpDirPf; //temporary folder to move files to, before moving whole folder to recycler (postfixed with file name separator)
- const Zstring versioningDirPf; //timestamped versioning folder
+ const Zstring versioningDir_;
+ const TimeComp timeStamp_;
+ const int versionCountLimit_;
+ Zstring recyclerTmpDirPf; //temporary folder to move files to, before moving whole folder to recycler (postfixed with file name separator)
#ifdef FFS_WIN
bool recFallbackDelPermantently;
@@ -506,6 +478,7 @@ private:
//magage three states: allow dynamic fallback from recycler to permanent deletion
DeletionPolicy deletionPolicy_;
+ std::unique_ptr<FileVersioner> versioner; //used for DELETE_TO_VERSIONING; throw FileError in constructor => create on demand!
//buffer status texts:
std::wstring txtRemovingFile;
@@ -516,31 +489,43 @@ private:
};
-DeletionHandling::DeletionHandling(DeletionPolicy handleDel,
- const Zstring& custDelDir, // final custom deletion directory: custDelDir + \ + subdirShort
- const Zstring& subdirShort, //
+DeletionHandling::DeletionHandling(DeletionPolicy handleDel, //nothrow!
+ const Zstring& versioningDir,
+ const TimeComp& timeStamp,
+ int versionCountLimit,
+ size_t folderIndex,
const Zstring& baseDirPf, //with separator postfix
ProcessCallback& procCallback) :
- procCallback_(&procCallback),
+ procCallback_(procCallback),
baseDirPf_(baseDirPf),
- custDelDir_(custDelDir),
- recyclerTmpDirPf(!baseDirPf_.empty() ? appendSeparator(findUnusedName(baseDirPf_ + Zstr("FFS ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S")))) : Zstring()),
- versioningDirPf (!custDelDir.empty() ? appendSeparator(findUnusedName(appendSeparator(custDelDir) + subdirShort)) : Zstring()),
+ versioningDir_(versioningDir),
+ timeStamp_(timeStamp),
+ versionCountLimit_(versionCountLimit),
#ifdef FFS_WIN
recFallbackDelPermantently(false),
#endif
- deletionPolicy_(MOVE_TO_RECYCLE_BIN),
+ deletionPolicy_(DELETE_TO_RECYCLER),
cleanedUp(false)
{
#ifdef FFS_WIN
if (!baseDirPf.empty())
- if (handleDel == MOVE_TO_RECYCLE_BIN && recycleBinStatus(baseDirPf) != STATUS_REC_EXISTS)
+ if (handleDel == DELETE_TO_RECYCLER && recycleBinStatus(baseDirPf) != STATUS_REC_EXISTS)
{
handleDel = DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks)
recFallbackDelPermantently = true;
}
#endif
+ //assemble temporary recycler bin directory
+ if (!baseDirPf_.empty())
+ {
+ Zstring tempDir = baseDirPf_ + Zstr("FFS ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp);
+ if (folderIndex > 0)
+ tempDir += Zstr(" [") + numberTo<Zstring>(folderIndex + 1) + Zstr("]"); //e.g. C:\Source\FFS 2012-05-15 131513 [2]
+
+ recyclerTmpDirPf = appendSeparator(tempDir);
+ }
+
setDeletionPolicy(handleDel);
}
@@ -557,16 +542,16 @@ void DeletionHandling::setDeletionPolicy(DeletionPolicy newPolicy)
txtRemovingSymlink = _("Deleting symbolic link %x");
break;
- case MOVE_TO_RECYCLE_BIN:
+ case DELETE_TO_RECYCLER:
txtRemovingFile = _("Moving file %x to recycle bin" );
txtRemovingDirectory = _("Moving folder %x to recycle bin" );
txtRemovingSymlink = _("Moving symbolic link %x to recycle bin");
break;
- case MOVE_TO_CUSTOM_DIRECTORY:
- txtRemovingFile = replaceCpy(_("Moving file %x to %y" ), L"%y", fmtFileName(custDelDir_));
- txtRemovingDirectory = replaceCpy(_("Moving folder %x to %y" ), L"%y", fmtFileName(custDelDir_));
- txtRemovingSymlink = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", fmtFileName(custDelDir_));
+ case DELETE_TO_VERSIONING:
+ txtRemovingFile = replaceCpy(_("Moving file %x to %y" ), L"%y", fmtFileName(versioningDir_));
+ txtRemovingDirectory = replaceCpy(_("Moving folder %x to %y" ), L"%y", fmtFileName(versioningDir_));
+ txtRemovingSymlink = replaceCpy(_("Moving symbolic link %x to %y"), L"%y", fmtFileName(versioningDir_));
break;
}
}
@@ -576,9 +561,25 @@ void DeletionHandling::tryCleanup() //throw FileError
{
if (!cleanedUp)
{
- if (deletionPolicy_ == MOVE_TO_RECYCLE_BIN) //clean-up temporary directory (recycle bin)
- if (!recyclerTmpDirPf.empty()) //empty folder input pair
- recycleOrDelete(beforeLast(recyclerTmpDirPf, FILE_NAME_SEPARATOR)); //throw FileError
+ switch (deletionPolicy_)
+ {
+ case DELETE_PERMANENTLY:
+ break;
+
+ case DELETE_TO_RECYCLER:
+ //clean-up temporary directory (recycle bin)
+ if (!recyclerTmpDirPf.empty()) //folder input pair may be empty
+ recycleOrDelete(beforeLast(recyclerTmpDirPf, FILE_NAME_SEPARATOR)); //throw FileError
+ break;
+
+ case DELETE_TO_VERSIONING:
+ if (versioner.get())
+ {
+ procCallback_.reportStatus(_("Removing old versions..."));
+ versioner->limitVersions([&] { procCallback_.requestUiRefresh(); }); //throw FileError
+ }
+ break;
+ }
cleanedUp = true;
}
@@ -587,18 +588,17 @@ void DeletionHandling::tryCleanup() //throw FileError
namespace
{
-class CallbackMoveFileImpl : public CallbackMoveFile //callback functionality
+class CallbackMoveFileImpl : public CallbackMoveFile
{
public:
CallbackMoveFileImpl(ProcessCallback& statusHandler,
- const DeletionHandling& delHandling,
int* objectsReported) :
statusHandler_ (statusHandler),
- delHandling_ (delHandling),
objectsReported_(objectsReported),
- txtMovingFile (_("Moving file %x to %y" )),
- txtMovingFolder (_("Moving folder %x to %y")) {}
+ txtMovingFile (_("Moving file %x to %y")),
+ txtMovingFolder (_("Moving folder %x to %y")) {}
+private:
virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) { reportStatus(txtMovingFile, fileFrom, fileTo); }
virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) { reportStatus(txtMovingFolder, dirFrom, dirTo); }
virtual void objectProcessed() //one call after each processed move
@@ -618,14 +618,12 @@ public:
statusHandler_.requestUiRefresh();
}
-private:
- void reportStatus(const std::wstring& rawText, const Zstring& fileFrom, const Zstring& fileTo) const
+ void reportStatus(const std::wstring& statusText, const Zstring& fileFrom, const Zstring& fileTo) const
{
- statusHandler_.reportStatus(replaceCpy(replaceCpy(rawText, L"%x", fmtFileName(fileFrom)), L"%y", fmtFileName(fileTo)));
+ statusHandler_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", fmtFileName(fileFrom)), L"%y", fmtFileName(fileTo)));
};
ProcessCallback& statusHandler_;
- const DeletionHandling& delHandling_;
int* objectsReported_; //optional
const std::wstring txtMovingFile;
@@ -642,11 +640,11 @@ struct CallbackRemoveDirImpl : public CallbackRemoveDir
delHandling_(delHandling),
objectsReported_(objectsReported) {}
- virtual void notifyFileDeletion(const Zstring& filename) { processSingleObject(filename, delHandling_.getTxtRemovingFile()); }
- virtual void notifyDirDeletion (const Zstring& dirname ) { processSingleObject(dirname, delHandling_.getTxtRemovingDir ()); }
-
private:
- void processSingleObject(const Zstring& objName, const std::wstring& statusText)
+ virtual void notifyFileDeletion(const Zstring& filename) { processSingleObject(delHandling_.getTxtRemovingFile(), filename); }
+ virtual void notifyDirDeletion (const Zstring& dirname ) { processSingleObject(delHandling_.getTxtRemovingDir (), dirname ); }
+
+ void processSingleObject(const std::wstring& statusText, const Zstring& objName)
{
statusHandler_.reportStatus(replaceCpy(statusText, L"%x", fmtFileName(objName)));
@@ -664,7 +662,7 @@ private:
}
-void DeletionHandling::removeFile(const Zstring& relativeName) const
+void DeletionHandling::removeFile(const Zstring& relativeName)
{
const Zstring fullName = baseDirPf_ + relativeName;
@@ -672,10 +670,10 @@ void DeletionHandling::removeFile(const Zstring& relativeName) const
{
case DELETE_PERMANENTLY:
zen::removeFile(fullName);
- //[!] resolve nameclash!
+ //[!] scope specifier resolves nameclash!
break;
- case MOVE_TO_RECYCLE_BIN:
+ case DELETE_TO_RECYCLER:
{
const Zstring targetFile = recyclerTmpDirPf + relativeName; //ends with path separator
@@ -687,7 +685,7 @@ void DeletionHandling::removeFile(const Zstring& relativeName) const
}
catch (FileError&)
{
- if (fileExists(fullName))
+ if (somethingExists(fullName)) //no file at all is not an error (however a directory is *not* expected!)
try
{
const Zstring targetDir = beforeLast(targetFile, FILE_NAME_SEPARATOR);
@@ -707,53 +705,34 @@ void DeletionHandling::removeFile(const Zstring& relativeName) const
}
break;
- case MOVE_TO_CUSTOM_DIRECTORY:
+ case DELETE_TO_VERSIONING:
{
- CallbackMoveFileImpl callBack(*procCallback_, *this, nullptr); //we do *not* report statistics in this method
- const Zstring targetFile = versioningDirPf + relativeName; //ends with path separator
-
- try //... to get away cheaply!
- {
- moveFile(fullName, targetFile, &callBack); //throw FileError
- }
- catch (FileError&)
- {
- if (fileExists(fullName))
- {
- const Zstring targetDir = beforeLast(targetFile, FILE_NAME_SEPARATOR);
- if (!dirExists(targetDir))
- {
- makeDirectory(targetDir); //throw FileError
- moveFile(fullName, targetFile, &callBack); //throw FileError -> this should work now!
- }
- else
- throw;
- }
- }
+ CallbackMoveFileImpl callback(procCallback_, nullptr); //we do *not* report statistics in this method
+ getOrCreateVersioner().revisionFile(fullName, relativeName, callback); //throw FileError
}
break;
}
}
-void DeletionHandling::removeFolderInt(const Zstring& relativeName, const int* objectsExpected, const Int64* dataExpected) const //throw FileError
+void DeletionHandling::removeFolderInt(const Zstring& relativeName, const int* objectsExpected, const Int64* dataExpected) //throw FileError
{
const Zstring fullName = baseDirPf_ + relativeName;
int objectsReported = 0; //use *only* if "objectsExpected" is bound!
//in error situation: undo communication of processed amount of data
- ScopeGuard guardStatistics = makeGuard([&] { procCallback_->updateProcessedData(-objectsReported, 0); });
+ ScopeGuard guardStatistics = makeGuard([&] { procCallback_.updateProcessedData(-objectsReported, 0); });
switch (deletionPolicy_)
{
case DELETE_PERMANENTLY:
{
- CallbackRemoveDirImpl remDirCallback(*procCallback_, *this, objectsExpected ? &objectsReported : nullptr);
+ CallbackRemoveDirImpl remDirCallback(procCallback_, *this, objectsExpected ? &objectsReported : nullptr);
removeDirectory(fullName, &remDirCallback);
}
break;
- case MOVE_TO_RECYCLE_BIN:
+ case DELETE_TO_RECYCLER:
{
const Zstring targetDir = recyclerTmpDirPf + relativeName;
@@ -765,7 +744,7 @@ void DeletionHandling::removeFolderInt(const Zstring& relativeName, const int* o
}
catch (FileError&)
{
- if (dirExists(fullName))
+ if (somethingExists(fullName))
try
{
const Zstring targetSuperDir = beforeLast(targetDir, FILE_NAME_SEPARATOR);
@@ -786,34 +765,15 @@ void DeletionHandling::removeFolderInt(const Zstring& relativeName, const int* o
if (objectsExpected) //even though we have only one disk access, we completed "objectsExpected" logical operations!
{
- procCallback_->updateProcessedData(*objectsExpected, 0);
+ procCallback_.updateProcessedData(*objectsExpected, 0);
objectsReported += *objectsExpected;
}
break;
- case MOVE_TO_CUSTOM_DIRECTORY:
+ case DELETE_TO_VERSIONING:
{
- CallbackMoveFileImpl callBack(*procCallback_, *this, objectsExpected ? &objectsReported : nullptr);
- const Zstring targetDir = versioningDirPf + relativeName;
-
- try //... to get away cheaply!
- {
- moveDirectory(fullName, targetDir, &callBack); //throw FileError
- }
- catch (FileError&)
- {
- if (dirExists(fullName))
- {
- const Zstring targetSuperDir = beforeLast(targetDir, FILE_NAME_SEPARATOR);
- if (!dirExists(targetSuperDir))
- {
- makeDirectory(targetSuperDir); //throw FileError
- moveDirectory(fullName, targetDir, &callBack); //throw FileError -> this should work now!
- }
- else
- throw;
- }
- }
+ CallbackMoveFileImpl callback(procCallback_, objectsExpected ? &objectsReported : nullptr);
+ getOrCreateVersioner().revisionDir(fullName, relativeName, callback); //throw FileError
}
break;
}
@@ -824,7 +784,7 @@ void DeletionHandling::removeFolderInt(const Zstring& relativeName, const int* o
guardStatistics.dismiss();
if (*objectsExpected != objectsReported || *dataExpected != 0) //adjust total: may have changed after comparison!
- procCallback_->updateTotalData(objectsReported - *objectsExpected, -*dataExpected);
+ procCallback_.updateTotalData(objectsReported - *objectsExpected, -*dataExpected);
}
}
@@ -836,15 +796,15 @@ bool DeletionHandling::deletionFreesSpace() const
{
case DELETE_PERMANENTLY:
return true;
- case MOVE_TO_RECYCLE_BIN:
+ case DELETE_TO_RECYCLER:
return false; //in general... (unless Recycle Bin is full)
- case MOVE_TO_CUSTOM_DIRECTORY:
- switch (zen::onSameVolume(baseDirPf_, versioningDirPf))
+ case DELETE_TO_VERSIONING:
+ switch (zen::onSameVolume(baseDirPf_, versioningDir_))
{
case IS_SAME_YES:
return false;
case IS_SAME_NO:
- return true; //but other volume (sessionDelDir) may become full...
+ return true; //but other volume (versioningDir) may become full...
case IS_SAME_CANT_SAY:
return true; //a rough guess!
}
@@ -852,8 +812,8 @@ bool DeletionHandling::deletionFreesSpace() const
assert(false);
return true;
}
-//------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------
namespace
{
@@ -952,8 +912,8 @@ public:
#ifdef FFS_WIN
shadow::ShadowCopy* shadowCopyHandler,
#endif
- const DeletionHandling& delHandlingLeft,
- const DeletionHandling& delHandlingRight) :
+ DeletionHandling& delHandlingLeft,
+ DeletionHandling& delHandlingRight) :
procCallback_(procCallback),
#ifdef FFS_WIN
shadowCopyHandler_(shadowCopyHandler),
@@ -1002,14 +962,14 @@ private:
template <PassId pass>
void runPass(HierarchyObject& hierObj);
- void synchronizeFile(FileMapping& fileObj) const;
- template <SelectedSide side> void synchronizeFileInt(FileMapping& fileObj, SyncOperation syncOp) const;
+ void synchronizeFile(FileMapping& fileObj);
+ template <SelectedSide side> void synchronizeFileInt(FileMapping& fileObj, SyncOperation syncOp);
- void synchronizeLink(SymLinkMapping& linkObj) const;
- template <SelectedSide sideTrg> void synchronizeLinkInt(SymLinkMapping& linkObj, SyncOperation syncOp) const;
+ void synchronizeLink(SymLinkMapping& linkObj);
+ template <SelectedSide sideTrg> void synchronizeLinkInt(SymLinkMapping& linkObj, SyncOperation syncOp);
- void synchronizeFolder(DirMapping& dirObj) const;
- template <SelectedSide sideTrg> void synchronizeFolderInt(DirMapping& dirObj, SyncOperation syncOp) const;
+ void synchronizeFolder(DirMapping& dirObj);
+ template <SelectedSide sideTrg> void synchronizeFolderInt(DirMapping& dirObj, SyncOperation syncOp);
void reportInfo (const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportInfo (replaceCpy(rawText, L"%x", fmtFileName(objname))); };
void reportStatus(const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportStatus(replaceCpy(rawText, L"%x", fmtFileName(objname))); };
@@ -1020,14 +980,14 @@ private:
void verifyFileCopy(const Zstring& source, const Zstring& target) const;
template <SelectedSide side>
- const DeletionHandling& getDelHandling() const;
+ DeletionHandling& getDelHandling();
ProcessCallback& procCallback_;
#ifdef FFS_WIN
shadow::ShadowCopy* shadowCopyHandler_; //optional!
#endif
- const DeletionHandling& delHandlingLeft_;
- const DeletionHandling& delHandlingRight_;
+ DeletionHandling& delHandlingLeft_;
+ DeletionHandling& delHandlingRight_;
const bool verifyCopiedFiles_;
const bool copyFilePermissions_;
@@ -1047,10 +1007,10 @@ private:
namespace zen
{
template <> inline
-const DeletionHandling& SynchronizeFolderPair::getDelHandling<LEFT_SIDE>() const { return delHandlingLeft_; }
+DeletionHandling& SynchronizeFolderPair::getDelHandling<LEFT_SIDE>() { return delHandlingLeft_; }
template <> inline
-const DeletionHandling& SynchronizeFolderPair::getDelHandling<RIGHT_SIDE>() const { return delHandlingRight_; }
+DeletionHandling& SynchronizeFolderPair::getDelHandling<RIGHT_SIDE>() { return delHandlingRight_; }
}
@@ -1458,7 +1418,7 @@ bool getTargetDirection(SyncOperation syncOp, SelectedSide* side)
inline
-void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
+void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj)
{
const SyncOperation syncOp = fileObj.getSyncOperation();
@@ -1475,7 +1435,7 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
template <SelectedSide sideTrg>
-void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperation syncOp) const
+void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperation syncOp)
{
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
@@ -1611,7 +1571,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati
inline
-void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
+void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj)
{
const SyncOperation syncOp = linkObj.getSyncOperation();
@@ -1628,7 +1588,7 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
template <SelectedSide sideTrg>
-void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOperation syncOp) const
+void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOperation syncOp)
{
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
@@ -1735,7 +1695,7 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper
inline
-void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
+void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj)
{
const SyncOperation syncOp = dirObj.getSyncOperation();
@@ -1752,7 +1712,7 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
template <SelectedSide sideTrg>
-void SynchronizeFolderPair::synchronizeFolderInt(DirMapping& dirObj, SyncOperation syncOp) const
+void SynchronizeFolderPair::synchronizeFolderInt(DirMapping& dirObj, SyncOperation syncOp)
{
static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
@@ -1860,9 +1820,23 @@ struct LessDependentDirectory : public std::binary_function<Zstring, Zstring, bo
}
-void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCfg>& syncConfig, FolderComparison& folderCmp)
+void zen::synchronize(const TimeComp& timeStamp,
+ xmlAccess::OptionalDialogs& warnings,
+ bool verifyCopiedFiles,
+ bool copyLockedFiles,
+ bool copyFilePermissions,
+ bool transactionalFileCopy,
+ bool runWithBackgroundPriority,
+ const std::vector<FolderPairSyncCfg>& syncConfig,
+ FolderComparison& folderCmp,
+ ProcessCallback& callback)
{
- //prevent shutdown while synchronization is in progress
+ //specify process and resource handling priorities
+ std::unique_ptr<ScheduleForBackgroundProcessing> backgroundPrio;
+ if (runWithBackgroundPriority)
+ backgroundPrio.reset(new ScheduleForBackgroundProcessing);
+
+ //prevent operating system going into sleep state
PreventStandby dummy;
(void)dummy;
@@ -1875,38 +1849,40 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
const SyncStatistics statisticsTotal(folderCmp);
//keep at beginning so that all gui elements are initialized properly
- procCallback.initNewPhase(getCUD(statisticsTotal),
- statisticsTotal.getDataToProcess(),
- ProcessCallback::PHASE_SYNCHRONIZING);
+ callback.initNewPhase(getCUD(statisticsTotal),
+ statisticsTotal.getDataToProcess(),
+ ProcessCallback::PHASE_SYNCHRONIZING);
std::deque<bool> skipFolderPair(folderCmp.size()); //folder pairs may be skipped after fatal errors were found
//initialize deletion handling: already required when checking for warnings
- FixedList<std::pair<DeletionHandling, DeletionHandling>> delHandler;
+ FixedList<DeletionHandling> delHandlerL; //we can't use a FixedList<std::pair<>> because DeletionHandling is not copy-constructable
+ FixedList<DeletionHandling> delHandlerR;
for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
{
const size_t folderIndex = j - folderCmp.begin();
const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex];
- const Zstring subDirShort = folderCmp.size() <= 1 ? custDelDirShortname : custDelDirShortname + Zstr(" (") + numberTo<Zstring>(folderIndex + 1) + Zstr(")");
- //e.g. "SyncJob 2012-05-15 131513 (1)" -> enforce different custom deletion dir when using multiple folder pairs!!!
-
- delHandler.emplace_back(DeletionHandling(folderPairCfg.handleDeletion,
- folderPairCfg.custDelFolder,
- subDirShort,
- j->getBaseDirPf<LEFT_SIDE>(),
- procCallback),
-
- DeletionHandling(folderPairCfg.handleDeletion,
- folderPairCfg.custDelFolder,
- subDirShort,
- j->getBaseDirPf<RIGHT_SIDE>(),
- procCallback));
+ delHandlerL.emplace_back(folderPairCfg.handleDeletion,
+ folderPairCfg.versioningFolder,
+ timeStamp,
+ folderPairCfg.versionCountLimit_,
+ folderIndex,
+ j->getBaseDirPf<LEFT_SIDE>(),
+ callback);
+
+ delHandlerR.emplace_back(folderPairCfg.handleDeletion,
+ folderPairCfg.versioningFolder,
+ timeStamp,
+ folderPairCfg.versionCountLimit_,
+ folderIndex,
+ j->getBaseDirPf<RIGHT_SIDE>(),
+ callback);
}
- //-------------------execute basic all at once before starting sync--------------------------------------
+ //-------------------execute basic checks all at once before starting sync--------------------------------------
auto dependentDir = [](const Zstring& lhs, const Zstring& rhs) //note: this is NOT an equivalence relation!
{
@@ -1949,8 +1925,9 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//start checking folder pairs
{
- auto iterDelHandler = delHandler.cbegin();
- for (auto j = begin(folderCmp); j != end(folderCmp); ++j, ++iterDelHandler)
+ auto iterDelHandlerL = delHandlerL.cbegin();
+ auto iterDelHandlerR = delHandlerR.cbegin();
+ for (auto j = begin(folderCmp); j != end(folderCmp); ++j, ++iterDelHandlerL, ++iterDelHandlerR)
{
const size_t folderIndex = j - begin(folderCmp);
@@ -1984,7 +1961,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
if ((j->getBaseDirPf<LEFT_SIDE >().empty() && (writeLeft || folderPairCfg.inAutomaticMode)) ||
(j->getBaseDirPf<RIGHT_SIDE>().empty() && (writeRight || folderPairCfg.inAutomaticMode)))
{
- procCallback.reportFatalError(_("Target folder input field must not be empty."));
+ callback.reportFatalError(_("Target folder input field must not be empty."));
skipFolderPair[folderIndex] = true;
continue;
}
@@ -2016,12 +1993,12 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
if (folderPairStat.getUpdate() + folderPairStat.getDelete() > 0 &&
- folderPairCfg.handleDeletion == zen::MOVE_TO_CUSTOM_DIRECTORY)
+ folderPairCfg.handleDeletion == zen::DELETE_TO_VERSIONING)
{
//check if user-defined directory for deletion was specified
- if (folderPairCfg.custDelFolder.empty()) //already trimmed by getFormattedDirectoryName()
+ if (folderPairCfg.versioningFolder.empty()) //already trimmed by getFormattedDirectoryName()
{
- procCallback.reportFatalError(_("Folder input field for versioning must not be empty."));
+ callback.reportFatalError(_("Folder input field for versioning must not be empty."));
skipFolderPair[folderIndex] = true;
continue;
}
@@ -2042,7 +2019,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
{
if (!wasExisting) //avoid race-condition: we need to evaluate existence status from time of comparison!
{
- procCallback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtFileName(baseDirPf)));
+ callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtFileName(baseDirPf)));
skipFolderPair[folderIndex] = true;
return false;
}
@@ -2072,23 +2049,23 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
catch (FileError&) {}
};
const std::pair<Int64, Int64> spaceNeeded = DiskSpaceNeeded::calculate(*j,
- iterDelHandler->first .deletionFreesSpace(),
- iterDelHandler->second.deletionFreesSpace());
+ iterDelHandlerL->deletionFreesSpace(),
+ iterDelHandlerR->deletionFreesSpace());
checkSpace(j->getBaseDirPf<LEFT_SIDE >(), spaceNeeded.first);
checkSpace(j->getBaseDirPf<RIGHT_SIDE>(), spaceNeeded.second);
#ifdef FFS_WIN
//windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
- if (folderPairCfg.handleDeletion == MOVE_TO_RECYCLE_BIN)
+ if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER)
{
if (folderPairStat.getUpdate<LEFT_SIDE>() +
folderPairStat.getDelete<LEFT_SIDE>() > 0 &&
- iterDelHandler->first.recyclerFallbackOnDelete())
+ iterDelHandlerL->recyclerFallbackOnDelete())
recyclMissing.insert(j->getBaseDirPf<LEFT_SIDE>());
if (folderPairStat.getUpdate<RIGHT_SIDE>() +
folderPairStat.getDelete<RIGHT_SIDE>() > 0 &&
- iterDelHandler->second.recyclerFallbackOnDelete())
+ iterDelHandlerR->recyclerFallbackOnDelete())
recyclMissing.insert(j->getBaseDirPf<RIGHT_SIDE>());
}
#endif
@@ -2111,7 +2088,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
warningMessage += _("You can ignore conflicts and continue synchronization.");
- procCallback.reportWarning(warningMessage, m_warnings.warningUnresolvedConflicts);
+ callback.reportWarning(warningMessage, warnings.warningUnresolvedConflicts);
}
@@ -2127,7 +2104,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
warningMessage += L"\n\n";
warningMessage += _("More than 50% of the total number of files will be copied or deleted!");
- procCallback.reportWarning(warningMessage, m_warnings.warningSignificantDifference);
+ callback.reportWarning(warningMessage, warnings.warningSignificantDifference);
}
@@ -2142,7 +2119,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
_("Required:") + L" " + filesizeToShortString(i->second.first) + L"\n" +
_("Available:") + L" " + filesizeToShortString(i->second.second);
- procCallback.reportWarning(warningMessage, m_warnings.warningNotEnoughDiskSpace);
+ callback.reportWarning(warningMessage, warnings.warningNotEnoughDiskSpace);
}
@@ -2156,7 +2133,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
std::for_each(recyclMissing.begin(), recyclMissing.end(),
[&](const Zstring& path) { warningMessage += L"\n" + utfCvrtTo<std::wstring>(path); });
- procCallback.reportWarning(warningMessage, m_warnings.warningRecyclerMissing);
+ callback.reportWarning(warningMessage, warnings.warningRecyclerMissing);
}
#endif
@@ -2175,21 +2152,22 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
std::wstring warningMessage = _("A folder will be modified which is part of multiple folder pairs. Please review synchronization settings.") + L"\n";
for (auto i = conflictDirs.begin(); i != conflictDirs.end(); ++i)
warningMessage += L"\n" + fmtFileName(*i);
- procCallback.reportWarning(warningMessage, m_warnings.warningMultiFolderWriteAccess);
+ callback.reportWarning(warningMessage, warnings.warningMultiFolderWriteAccess);
}
//-------------------end of basic checks------------------------------------------
#ifdef FFS_WIN
//shadow copy buffer: per sync-instance, not folder pair
- std::unique_ptr<shadow::ShadowCopy> shadowCopyHandler(copyLockedFiles_ ? new shadow::ShadowCopy : nullptr);
+ std::unique_ptr<shadow::ShadowCopy> shadowCopyHandler(copyLockedFiles ? new shadow::ShadowCopy : nullptr);
#endif
try
{
//loop through all directory pairs
- auto iterDelHandler = delHandler.begin();
- for (auto j = begin(folderCmp); j != end(folderCmp); ++j, ++iterDelHandler)
+ auto iterDelHandlerL = delHandlerL.begin();
+ auto iterDelHandlerR = delHandlerR.begin();
+ for (auto j = begin(folderCmp); j != end(folderCmp); ++j, ++iterDelHandlerL, ++iterDelHandlerR)
{
//exclude some pathological case (leftdir, rightdir are empty)
if (EqualFilename()(j->getBaseDirPf<LEFT_SIDE>(), j->getBaseDirPf<RIGHT_SIDE>()))
@@ -2201,9 +2179,9 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
std::wstring right = _("Right") + L": ";
makeSameLength(left, right);
- procCallback.reportInfo(_("Synchronize folder pair:") + L"\n" +
- L" " + left + fmtFileName(j->getBaseDirPf<LEFT_SIDE >()) + L"\n" +
- L" " + right + fmtFileName(j->getBaseDirPf<RIGHT_SIDE>()));
+ callback.reportInfo(_("Synchronizing folder pair:") + L"\n" +
+ L" " + left + fmtFileName(j->getBaseDirPf<LEFT_SIDE >()) + L"\n" +
+ L" " + right + fmtFileName(j->getBaseDirPf<RIGHT_SIDE>()));
//------------------------------------------------------------------------------------------
const size_t folderIndex = j - begin(folderCmp);
@@ -2223,9 +2201,9 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//just convenience: exit sync right here instead of showing tons of error messages during file copy
return tryReportingError([&]
{
- if (!dirExistsUpdating(dirname, false, procCallback))
+ if (!dirExistsUpdating(dirname, false, callback))
throw FileError(replaceCpy(_("Cannot find folder %x."), L"%x", fmtFileName(dirname))); //this should really be a "fatal error"
- }, procCallback); //may throw in error-callback!
+ }, callback); //may throw in error-callback!
}
else //create target directory: user presumably ignored error "dir existing" in order to have it created automatically
{
@@ -2241,7 +2219,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
{
//TEMPORARY network drop: base directory not found during comparison, but reappears during synchronization
//=> sync-directions are based on false assumptions! Abort.
- procCallback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(baseDirPf)));
+ callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(baseDirPf)));
temporaryNetworkDrop = true;
//Is it possible we're catching a "false-positive" here, could FFS have created the directory indirectly after comparison?
@@ -2249,7 +2227,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
// 2. deletion handling: versioning -> "
// 3. log file creates containing folder -> no, log only created in batch mode, and only *before* comparison
}
- }, procCallback); //may throw in error-callback!
+ }, callback); //may throw in error-callback!
return rv && !temporaryNetworkDrop;
}
}
@@ -2276,36 +2254,38 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
bool copyPermissionsFp = false;
tryReportingError([&]
{
- copyPermissionsFp = copyFilePermissions_ && //copy permissions only if asked for and supported by *both* sides!
+ copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides!
+ !j->getBaseDirPf<LEFT_SIDE >().empty() && //scenario: directory selected on one side only
+ !j->getBaseDirPf<RIGHT_SIDE>().empty() && //
supportsPermissions(beforeLast(j->getBaseDirPf<LEFT_SIDE >(), FILE_NAME_SEPARATOR)) && //throw FileError
supportsPermissions(beforeLast(j->getBaseDirPf<RIGHT_SIDE>(), FILE_NAME_SEPARATOR));
- }, procCallback); //show error dialog if necessary
+ }, callback); //show error dialog if necessary
- SynchronizeFolderPair syncFP(procCallback, verifyCopiedFiles_, copyPermissionsFp, transactionalFileCopy_,
+ SynchronizeFolderPair syncFP(callback, verifyCopiedFiles, copyPermissionsFp, transactionalFileCopy,
#ifdef FFS_WIN
shadowCopyHandler.get(),
#endif
- iterDelHandler->first, iterDelHandler->second);
+ *iterDelHandlerL, *iterDelHandlerR);
syncFP.startSync(*j);
- //(try to gracefully) cleanup temporary folders (Recycle bin optimization) -> will be done in ~DeletionHandling anyway...
- tryReportingError([&] { iterDelHandler->first .tryCleanup(); }, procCallback); //show error dialog if necessary
- tryReportingError([&] { iterDelHandler->second.tryCleanup(); }, procCallback); //
+ //(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway...
+ tryReportingError([&] { iterDelHandlerL->tryCleanup(); }, callback); //show error dialog if necessary
+ tryReportingError([&] { iterDelHandlerR->tryCleanup(); }, callback); //
//(try to gracefully) write database file (will be done in ~EnforceUpdateDatabase anyway...)
if (folderPairCfg.inAutomaticMode)
{
- procCallback.reportStatus(_("Generating database..."));
- procCallback.forceUiRefresh();
+ callback.reportStatus(_("Generating database..."));
+ callback.forceUiRefresh();
- tryReportingError([&] { zen::saveLastSynchronousState(*j); }, procCallback); //throw FileError
+ tryReportingError([&] { zen::saveLastSynchronousState(*j); }, callback); //throw FileError
guardUpdateDb.dismiss();
}
}
}
catch (const std::exception& e)
{
- procCallback.reportFatalError(utfCvrtTo<std::wstring>(e.what()));
+ callback.reportFatalError(utfCvrtTo<std::wstring>(e.what()));
}
}
@@ -2314,7 +2294,7 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//callback functionality for smooth progress indicators
template <class DelTargetCommand>
-class WhileCopying : public zen::CallbackCopyFile //callback functionality
+class WhileCopying : public zen::CallbackCopyFile
{
public:
WhileCopying(Int64& bytesReported,
@@ -2420,15 +2400,12 @@ void SynchronizeFolderPair::copyFileUpdatingTo(const FileMapping& fileObj, const
//--------------------- data verification -------------------------
-
-//callback functionality for status updates while verifying
struct VerifyCallback
{
virtual ~VerifyCallback() {}
virtual void updateStatus() = 0;
};
-
void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& callback) // throw (FileError)
{
static std::vector<char> memory1(1024 * 1024); //1024 kb seems to be a reasonable buffer size
bgstack15