summaryrefslogtreecommitdiff
path: root/synchronization.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:11:09 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:11:09 +0200
commit9cc790869ed3905c78c7eeeb0bb44f800b3f2af4 (patch)
tree1c085bbf2302be294866c4fc6e0d225f8abbc346 /synchronization.cpp
parent3.14 (diff)
downloadFreeFileSync-9cc790869ed3905c78c7eeeb0bb44f800b3f2af4.tar.gz
FreeFileSync-9cc790869ed3905c78c7eeeb0bb44f800b3f2af4.tar.bz2
FreeFileSync-9cc790869ed3905c78c7eeeb0bb44f800b3f2af4.zip
3.15
Diffstat (limited to 'synchronization.cpp')
-rw-r--r--synchronization.cpp343
1 files changed, 210 insertions, 133 deletions
diff --git a/synchronization.cpp b/synchronization.cpp
index 77b734eb..f98e15f6 100644
--- a/synchronization.cpp
+++ b/synchronization.cpp
@@ -6,7 +6,6 @@
//
#include "synchronization.h"
#include <stdexcept>
-#include <wx/intl.h>
#include <wx/msgdlg.h>
#include <wx/log.h>
#include "shared/string_conv.h"
@@ -14,7 +13,9 @@
#include "shared/system_constants.h"
#include "library/status_handler.h"
#include "shared/file_handling.h"
+#include "shared/resolve_path.h"
#include "shared/recycler.h"
+#include "shared/i18n.h"
#include <wx/file.h>
#include <boost/bind.hpp>
#include "shared/global_func.h"
@@ -30,6 +31,7 @@
#include "shared/long_path_prefix.h"
#include <boost/scoped_ptr.hpp>
#include "shared/perf.h"
+#include "shared/shadow.h"
#endif
using namespace ffs3;
@@ -519,25 +521,6 @@ SyncProcess::SyncProcess(xmlAccess::OptionalDialogs& warnings,
//--------------------------------------------------------------------------------------------------------------
-namespace
-{
-void ensureExists(const Zstring& dirname, const Zstring& templateDir, bool copyFilePermissions) //throw (FileError)
-{
- if (!dirname.empty()) //kind of pathological ?
- if (!ffs3::dirExists(dirname))
- {
- //lazy creation of alternate deletion directory (including super-directories of targetFile)
- ffs3::createDirectory(dirname, templateDir, false, copyFilePermissions);
- }
- /*symbolic link handling:
- if "not traversing symlinks": fullName == c:\syncdir<symlinks>\some\dirs\leaf<symlink>
- => setting irrelevant
- if "traversing symlinks": fullName == c:\syncdir<symlinks>\some\dirs<symlinks>\leaf<symlink>
- => setting NEEDS to be false: We want to move leaf, therefore symlinks in "some\dirs" must not interfere */
-}
-}
-
-
class DeletionHandling
{
public:
@@ -690,21 +673,21 @@ const Zstring& DeletionHandling::getSessionDir<RIGHT_SIDE>() const
namespace
{
-class MoveFileCallbackImpl : public MoveFileCallback //callback functionality
+class CallbackMoveFileImpl : public CallbackMoveFile //callback functionality
{
public:
- MoveFileCallbackImpl(StatusHandler& handler) : statusHandler_(handler) {}
+ CallbackMoveFileImpl(StatusHandler& handler) : statusHandler_(handler) {}
virtual Response requestUiRefresh(const Zstring& currentObject) //DON'T throw exceptions here, at least in Windows build!
{
#ifdef FFS_WIN
statusHandler_.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
if (statusHandler_.abortIsRequested())
- return MoveFileCallback::CANCEL;
+ return CallbackMoveFile::CANCEL;
#elif defined FFS_LINUX
statusHandler_.requestUiRefresh(); //exceptions may be thrown here!
#endif
- return MoveFileCallback::CONTINUE;
+ return CallbackMoveFile::CONTINUE;
}
private:
@@ -712,9 +695,9 @@ private:
};
-struct RemoveDirCallbackImpl : public RemoveDirCallback
+struct CallbackRemoveDirImpl : public CallbackRemoveDir
{
- RemoveDirCallbackImpl(StatusHandler& handler) : statusHandler_(handler) {}
+ CallbackRemoveDirImpl(StatusHandler& handler) : statusHandler_(handler) {}
virtual void requestUiRefresh(const Zstring& currentObject)
{
@@ -770,8 +753,8 @@ void DeletionHandling::removeFile(const FileSystemObject& fileObj) const
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);
+ CallbackMoveFileImpl callBack(statusUpdater_); //if file needs to be copied we need callback functionality to update screen and offer abort
+ moveFile(fileObj.getFullName<side>(), targetFile, true, &callBack);
}
break;
}
@@ -787,7 +770,7 @@ void DeletionHandling::removeFolder(const FileSystemObject& dirObj) const
{
case DELETE_PERMANENTLY:
{
- RemoveDirCallbackImpl remDirCallback(statusUpdater_);
+ CallbackRemoveDirImpl remDirCallback(statusUpdater_);
removeDirectory(dirObj.getFullName<side>(), &remDirCallback);
}
break;
@@ -825,7 +808,7 @@ void DeletionHandling::removeFolder(const FileSystemObject& dirObj) const
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
+ CallbackMoveFileImpl 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;
@@ -929,8 +912,9 @@ private:
//more low level helper
template <ffs3::SelectedSide side>
void deleteSymlink(const SymLinkMapping& linkObj) const;
- void copySymlink(const Zstring& source, const Zstring& target, LinkDescriptor::LinkType type) const;
- void copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& sourceFileSize) const;
+ void copySymlink(const Zstring& source, const Zstring& target, LinkDescriptor::LinkType type, bool inRecursion = false) const;
+ template <class DelTargetCommand>
+ void copyFileUpdating(const Zstring& source, const Zstring& target, const DelTargetCommand& cmd, const wxULongLong& sourceFileSize, int recursionLvl = 0) const;
void verifyFileCopy(const Zstring& source, const Zstring& target) const;
StatusHandler& statusUpdater_;
@@ -1009,18 +993,35 @@ void SynchronizeFolderPair::execute(HierarchyObject& hierObj)
}
}
+
namespace
{
-//runtime impact per file: SSD: 0s, HDD: 43 µs, USB stick: 1 ms
-inline
-void checkFileReadable(const Zstring& filename) //throw (FileError)
+struct NullCommand
{
- ffs3::FileInput file(filename); //throw (FileError)
- char buffer[1];
- file.read(buffer, 1); //
-}
+ void operator()() const {}
+};
+
+
+template <SelectedSide side>
+class DelTargetCommand
+{
+public:
+ DelTargetCommand(FileMapping& fileObj, const DeletionHandling& delHandling) : fileObj_(fileObj), delHandling_(delHandling) {}
+
+ void operator()() const
+ {
+ //delete target and copy source
+ delHandling_.removeFile<side>(fileObj_); //throw (FileError)
+ fileObj_.removeObject<side>(); //remove file from FileMapping, to keep in sync (if subsequent copying fails!!)
+ }
+
+private:
+ FileMapping& fileObj_;
+ const DeletionHandling& delHandling_;
+};
}
+
void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
{
Zstring statusText;
@@ -1037,7 +1038,9 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target, fileObj.getFileSize<RIGHT_SIDE>());
+ copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target,
+ NullCommand(), //no target to delete
+ fileObj.getFileSize<RIGHT_SIDE>());
break;
case SO_CREATE_NEW_RIGHT:
@@ -1049,7 +1052,9 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target, fileObj.getFileSize<LEFT_SIDE>());
+ copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target,
+ NullCommand(), //no target to delete
+ fileObj.getFileSize<LEFT_SIDE>());
break;
case SO_DELETE_LEFT:
@@ -1058,7 +1063,7 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- delHandling_.removeFile<LEFT_SIDE>(fileObj); //throw FileError()
+ delHandling_.removeFile<LEFT_SIDE>(fileObj); //throw (FileError)
break;
case SO_DELETE_RIGHT:
@@ -1067,7 +1072,7 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- delHandling_.removeFile<RIGHT_SIDE>(fileObj); //throw FileError()
+ delHandling_.removeFile<RIGHT_SIDE>(fileObj); //throw (FileError)
break;
case SO_OVERWRITE_LEFT:
@@ -1079,14 +1084,9 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- //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)
-
- //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!!)
-
- copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target, fileObj.getFileSize<RIGHT_SIDE>());
+ copyFileUpdating(fileObj.getFullName<RIGHT_SIDE>(), target,
+ DelTargetCommand<LEFT_SIDE>(fileObj, delHandling_), //delete target at appropriate time
+ fileObj.getFileSize<RIGHT_SIDE>());
break;
case SO_OVERWRITE_RIGHT:
@@ -1098,14 +1098,9 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- //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)
-
- //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!!)
-
- copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target, fileObj.getFileSize<LEFT_SIDE>());
+ copyFileUpdating(fileObj.getFullName<LEFT_SIDE>(), target,
+ DelTargetCommand<RIGHT_SIDE>(fileObj, delHandling_), //delete target at appropriate time
+ fileObj.getFileSize<LEFT_SIDE>());
break;
case SO_COPY_METADATA_TO_LEFT:
@@ -1115,8 +1110,8 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
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);
+ renameFile(fileObj.getFullName<LEFT_SIDE>(),
+ fileObj.getFullName<LEFT_SIDE>().BeforeLast(common::FILE_NAME_SEPARATOR) + common::FILE_NAME_SEPARATOR + fileObj.getShortName<RIGHT_SIDE>()); //throw (FileError);
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)
@@ -1129,8 +1124,8 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj) const
statusUpdater_.requestUiRefresh(); //trigger display refresh
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);
+ renameFile(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)
@@ -1188,7 +1183,7 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- deleteSymlink<LEFT_SIDE>(linkObj); //throw FileError()
+ deleteSymlink<LEFT_SIDE>(linkObj); //throw (FileError)
break;
case SO_DELETE_RIGHT:
@@ -1197,7 +1192,7 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- deleteSymlink<RIGHT_SIDE>(linkObj); //throw FileError()
+ deleteSymlink<RIGHT_SIDE>(linkObj); //throw (FileError)
break;
case SO_OVERWRITE_LEFT:
@@ -1209,7 +1204,7 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- deleteSymlink<LEFT_SIDE>(linkObj); //throw FileError()
+ 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>());
@@ -1224,7 +1219,7 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- deleteSymlink<RIGHT_SIDE>(linkObj); //throw FileError()
+ 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>());
@@ -1237,8 +1232,8 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
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);
+ renameFile(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)
@@ -1251,8 +1246,8 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj) const
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);
+ renameFile(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)
@@ -1292,7 +1287,7 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
//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!
+ createDirectory(target, dirObj.getFullName<RIGHT_SIDE>(), copyFilePermissions_); //no symlink copying!
break;
case SO_CREATE_NEW_RIGHT:
@@ -1306,7 +1301,7 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
//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!
+ createDirectory(target, dirObj.getFullName<LEFT_SIDE>(), copyFilePermissions_); //no symlink copying!
break;
case SO_DELETE_LEFT:
@@ -1316,7 +1311,7 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- delHandling_.removeFolder<LEFT_SIDE>(dirObj); //throw FileError()
+ delHandling_.removeFolder<LEFT_SIDE>(dirObj); //throw (FileError)
{
//progress indicator update: DON'T forget to notify about implicitly deleted objects!
const SyncStatistics subObjects(dirObj);
@@ -1335,7 +1330,7 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
statusUpdater_.reportInfo(statusText);
statusUpdater_.requestUiRefresh(); //trigger display refresh
- delHandling_.removeFolder<RIGHT_SIDE>(dirObj); //throw FileError()
+ delHandling_.removeFolder<RIGHT_SIDE>(dirObj); //throw (FileError)
{
//progress indicator update: DON'T forget to notify about implicitly deleted objects!
const SyncStatistics subObjects(dirObj);
@@ -1354,8 +1349,8 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
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);
+ renameFile(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;
@@ -1366,8 +1361,8 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const
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);
+ renameFile(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;
@@ -1642,8 +1637,8 @@ 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") +
- _("Free disk space required:") + wxT(" ") + formatFilesizeToShortString(i->second.first) + wxT("\n") +
- _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(i->second.second);
+ _("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);
}
@@ -1792,13 +1787,18 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf
//###########################################################################################
//callback functionality for smooth progress indicators
-class WhileCopying : public ffs3::CopyFileCallback //callback functionality
+template <class DelTargetCommand>
+class WhileCopying : public ffs3::CallbackCopyFile //callback functionality
{
public:
+ WhileCopying(wxLongLong& bytesTransferredLast,
+ StatusHandler& statusHandler,
+ const DelTargetCommand& cmd) :
+ bytesTransferredLast_(bytesTransferredLast),
+ statusHandler_(statusHandler),
+ cmd_(cmd) {}
- WhileCopying(wxLongLong& bytesTransferredLast, StatusHandler& statusHandler) :
- m_bytesTransferredLast(bytesTransferredLast),
- m_statusHandler(statusHandler) {}
+ virtual void deleteTargetFile(const Zstring& targetFile) { cmd_(); }
virtual Response updateCopyStatus(const wxULongLong& totalBytesTransferred)
{
@@ -1806,88 +1806,165 @@ public:
const wxLongLong totalBytes = common::convertToSigned(totalBytesTransferred);
//inform about the (differential) processed amount of data
- m_statusHandler.updateProcessedData(0, totalBytes - m_bytesTransferredLast);
- m_bytesTransferredLast = totalBytes;
+ statusHandler_.updateProcessedData(0, totalBytes - bytesTransferredLast_);
+ bytesTransferredLast_ = totalBytes;
#ifdef FFS_WIN
- m_statusHandler.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
- if (m_statusHandler.abortIsRequested())
- return CopyFileCallback::CANCEL;
+ statusHandler_.requestUiRefresh(false); //don't allow throwing exception within this call: windows copying callback can't handle this
+ if (statusHandler_.abortIsRequested())
+ return CallbackCopyFile::CANCEL;
#elif defined FFS_LINUX
- m_statusHandler.requestUiRefresh(); //exceptions may be thrown here!
+ statusHandler_.requestUiRefresh(); //exceptions may be thrown here!
#endif
- return CopyFileCallback::CONTINUE;
+ return CallbackCopyFile::CONTINUE;
}
private:
- wxLongLong& m_bytesTransferredLast;
- StatusHandler& m_statusHandler;
+ wxLongLong& bytesTransferredLast_;
+ StatusHandler& statusHandler_;
+ DelTargetCommand cmd_;
};
-//copy file while executing statusUpdater->requestUiRefresh() calls
-void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstring& target, const wxULongLong& totalBytesToCpy) const
+class CleanUpStats //lambdas coming soon... unfortunately Loki::ScopeGuard is no option because of lazy function argument evaluation (-1 * ...)
{
- //create folders first (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080)
- const Zstring targetDir = target.BeforeLast(common::FILE_NAME_SEPARATOR);
- const Zstring templateDir = source.BeforeLast(common::FILE_NAME_SEPARATOR);
+ bool dismissed;
+ StatusHandler& statusUpdater_;
+ const wxLongLong& bytesTransferred_;
- ensureExists(targetDir, templateDir, copyFilePermissions_); //throw (FileError)
+public:
+ CleanUpStats(StatusHandler& statusUpdater, const wxLongLong& bytesTransferred) : dismissed(false), statusUpdater_(statusUpdater), bytesTransferred_(bytesTransferred) {}
+ ~CleanUpStats()
+ {
+ if (!dismissed)
+ try
+ {
+ statusUpdater_.updateProcessedData(0, bytesTransferred_ * -1); //throw ?
+ }
+ catch (...) {}
+ }
+ void dismiss() { dismissed = true; }
+};
- //start of (possibly) long-running copy process: ensure status updates are performed regularly
- wxLongLong totalBytesTransferred;
- WhileCopying callback(totalBytesTransferred, statusUpdater_);
+//copy file while executing statusUpdater->requestUiRefresh() calls
+template <class DelTargetCommand>
+void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstring& target, const DelTargetCommand& cmd, const wxULongLong& totalBytesToCpy, int recursionLvl) const
+{
+ const int exceptionPaths = 2;
try
{
- ffs3::copyFile(source,
+ //start of (possibly) long-running copy process: ensure status updates are performed regularly
+ wxLongLong totalBytesTransferred;
+ CleanUpStats dummy(statusUpdater_, totalBytesTransferred); //error situation: undo communication of processed amount of data
+
+ WhileCopying<DelTargetCommand> callback(totalBytesTransferred, statusUpdater_, cmd);
+
+ ffs3::copyFile(source, //type File implicitly means symlinks need to be dereferenced!
target,
- false, //type File implicitly means symlinks need to be dereferenced!
copyFilePermissions_,
+ &callback); //throw (FileError, ErrorFileLocked);
+
+ //inform about the (remaining) processed amount of data
+ dummy.dismiss();
+ statusUpdater_.updateProcessedData(0, common::convertToSigned(totalBytesToCpy) - totalBytesTransferred);
+ }
#ifdef FFS_WIN
- shadowCopyHandler_,
-#endif
- &callback);
+ catch (ErrorFileLocked&)
+ {
+ if (recursionLvl >= exceptionPaths) throw;
+
+ //if file is locked (try to) use Windows Volume Shadow Copy Service
+ if (shadowCopyHandler_ == NULL) throw;
- if (verifyCopiedFiles_) //verify if data was copied correctly
- verifyFileCopy(source, target);
+ Zstring shadowFilename;
+ try
+ {
+ //contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat"
+ shadowFilename = shadowCopyHandler_->makeShadowCopy(source); //throw (FileError)
+ }
+ catch (const FileError& e)
+ {
+ wxString errorMsg = _("Error copying locked file %x!");
+ errorMsg.Replace(wxT("%x"), wxString(wxT("\"")) + zToWx(source) + wxT("\""));
+ errorMsg += wxT("\n\n") + e.msg();
+ throw FileError(errorMsg);
+ }
+
+ //now try again
+ return copyFileUpdating(shadowFilename, target, cmd, totalBytesToCpy, recursionLvl + 1);
}
- catch (...)
+#endif
+ catch (ErrorTargetPathMissing&)
{
- //error situation: undo communication of processed amount of data
- statusUpdater_.updateProcessedData(0, totalBytesTransferred * -1 );
+ if (recursionLvl >= exceptionPaths) throw;
+
+ //create folders "first" (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080)
+ //using optimistic strategy: assume everything goes well, but cover up on error -> minimize file accesses
+ const Zstring targetDir = target.BeforeLast(common::FILE_NAME_SEPARATOR);
+ const Zstring templateDir = source.BeforeLast(common::FILE_NAME_SEPARATOR);
+
+ if (!targetDir.empty() && !ffs3::dirExists(targetDir))
+ {
+ ffs3::createDirectory(targetDir, templateDir, copyFilePermissions_); //throw (FileError)
+ /*symbolic link handling:
+ if "not traversing symlinks": fullName == c:\syncdir<symlinks>\some\dirs\leaf<symlink>
+ => setting irrelevant
+ if "traversing symlinks": fullName == c:\syncdir<symlinks>\some\dirs<symlinks>\leaf<symlink>
+ => setting NEEDS to be false: We want to move leaf, therefore symlinks in "some\dirs" must not interfere */
+
+ //now try again
+ return copyFileUpdating(source, target, cmd, totalBytesToCpy, recursionLvl + 1);
+ }
throw;
}
- //inform about the (remaining) processed amount of data
- statusUpdater_.updateProcessedData(0, common::convertToSigned(totalBytesToCpy) - totalBytesTransferred);
+ //todo: transactional behavior: delete target if verification fails?
+
+ if (verifyCopiedFiles_) //verify if data was copied correctly
+ verifyFileCopy(source, target); //throw (FileError)
}
-void SynchronizeFolderPair::copySymlink(const Zstring& source, const Zstring& target, LinkDescriptor::LinkType type) const
+void SynchronizeFolderPair::copySymlink(const Zstring& source, const Zstring& target, LinkDescriptor::LinkType type, bool inRecursion) const
{
- //create folders first (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080)
+ try
+ {
+ switch (type)
+ {
+ case LinkDescriptor::TYPE_DIR:
+ ffs3::copySymlink(source, target, SYMLINK_TYPE_DIR, copyFilePermissions_); //throw (FileError)
+ break;
- const Zstring targetDir = target.BeforeLast(common::FILE_NAME_SEPARATOR);
- const Zstring templateDir = source.BeforeLast(common::FILE_NAME_SEPARATOR);
+ case LinkDescriptor::TYPE_FILE: //Windows: true file symlink; Linux: file-link or broken link
+ ffs3::copySymlink(source, target, SYMLINK_TYPE_FILE, copyFilePermissions_); //throw (FileError)
+ break;
+ }
+ }
+ catch (FileError&)
+ {
+ if (inRecursion) throw;
- ensureExists(targetDir, templateDir, copyFilePermissions_); //throw (FileError)
+ //create folders "first" (see http://sourceforge.net/tracker/index.php?func=detail&aid=2628943&group_id=234430&atid=1093080)
+ //using optimistic strategy: assume everything goes well, but cover up on error -> minimize file accesses
+ const Zstring targetDir = target.BeforeLast(common::FILE_NAME_SEPARATOR);
+ const Zstring templateDir = source.BeforeLast(common::FILE_NAME_SEPARATOR);
- switch (type)
- {
- case LinkDescriptor::TYPE_DIR:
- ffs3::createDirectory(target, source, true, copyFilePermissions_); //copy symlink
- break;
+ if (!targetDir.empty() && !ffs3::dirExists(targetDir))
+ {
+ ffs3::createDirectory(targetDir, templateDir, copyFilePermissions_); //throw (FileError)
+ /*symbolic link handling:
+ if "not traversing symlinks": fullName == c:\syncdir<symlinks>\some\dirs\leaf<symlink>
+ => setting irrelevant
+ if "traversing symlinks": fullName == c:\syncdir<symlinks>\some\dirs<symlinks>\leaf<symlink>
+ => setting NEEDS to be false: We want to move leaf, therefore symlinks in "some\dirs" must not interfere */
+
+ //now try again
+ return copySymlink(source, target, type, true);
+ }
- 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_,
-#endif
- NULL);
- break;
+ throw;
}
}
bgstack15