summaryrefslogtreecommitdiff
path: root/synchronization.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'synchronization.cpp')
-rw-r--r--synchronization.cpp146
1 files changed, 80 insertions, 66 deletions
diff --git a/synchronization.cpp b/synchronization.cpp
index 227a48a8..0c04e68f 100644
--- a/synchronization.cpp
+++ b/synchronization.cpp
@@ -15,6 +15,7 @@
#include <zen/process_priority.h>
#include <zen/file_handling.h>
#include <zen/recycler.h>
+#include <zen/optional.h>
#include "lib/resolve_path.h"
#include "lib/db_file.h"
#include "lib/dir_exist_async.h"
@@ -499,7 +500,7 @@ Zstring createUniqueRandomTempDir(const Zstring& baseDirPf) //throw FileError
try
{
Zstring dirname = generatePath();
- makeNewDirectory(dirname, Zstring(), false); //FileError, ErrorTargetExisting
+ makeDirectory(dirname, /*bool failIfExists*/ true); //throw FileError, ErrorTargetExisting
return dirname;
}
catch (const ErrorTargetExisting&) {}
@@ -612,7 +613,7 @@ private:
void notifyMove(const std::wstring& statusText, const Zstring& fileFrom, const Zstring& fileTo) const
{
notifyItemDeletion_(); //it would be more correct to report *after* work was done!
- callback_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", fmtFileName(fileFrom)), L"%y", fmtFileName(fileTo)));
+ callback_.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", L"\n" + fmtFileName(fileFrom)), L"%y", L"\n" + fmtFileName(fileTo)));
};
virtual void updateStatus(Int64 bytesDelta)
@@ -673,7 +674,7 @@ void DeletionHandling::removeDirUpdating(const Zstring& relativeName, Int64 byte
{
if (somethingExists(fullName))
{
- const Zstring targetSuperDir = beforeLast(targetDir, FILE_NAME_SEPARATOR);
+ const Zstring targetSuperDir = beforeLast(targetDir, FILE_NAME_SEPARATOR); //what if C:\ ?
if (!dirExists(targetSuperDir))
{
makeDirectory(targetSuperDir); //throw FileError -> may legitimately fail on Linux if permissions are missing
@@ -987,8 +988,14 @@ private:
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))); };
+ void reportInfo (const std::wstring& rawText, const Zstring& objname) const { procCallback_.reportInfo (replaceCpy(rawText, L"%x", fmtFileName(objname))); };
+ void reportInfo (const std::wstring& rawText,
+ const Zstring& objname1,
+ const Zstring& objname2) const
+ {
+ procCallback_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtFileName(objname1)), L"%y", L"\n" + fmtFileName(objname2)));
+ };
template <SelectedSide sideTrg, class Function>
FileAttrib copyFileUpdatingTo(const FileMapping& fileObj, Function delTargetCommand) const; //throw FileError; reports data delta via updateProcessedData()
@@ -1090,7 +1097,7 @@ void SynchronizeFolderPair::prepare2StepMove(FileMapping& sourceObj,
//the very same (.ffs_tmp) name and is copied before the second step of the move is executed
//good news: even in this pathologic case, this may only prevent the copy of the other file, but not the move
- reportInfo(replaceCpy(txtMovingFile, L"%y", fmtFileName(tmpTarget)), source);
+ reportInfo(txtMovingFile, source, tmpTarget);
renameFile(source, tmpTarget); //throw FileError
@@ -1102,6 +1109,8 @@ void SynchronizeFolderPair::prepare2StepMove(FileMapping& sourceObj,
sourceObj.removeObject<side>();
FileMapping& tempFile = sourceObj.root().addSubFile<side>(afterLast(tmpTarget, FILE_NAME_SEPARATOR), descrSource);
+ static_assert(IsSameType<FixedList<FileMapping>, HierarchyObject::SubFileVec>::value,
+ "ATTENTION: we're adding to the file list WHILE looping over it! This is only working because FixedList iterators are not invalidated by this!");
//prepare move in second pass
tempFile.setSyncDir(side == LEFT_SIDE ? SYNC_DIR_LEFT : SYNC_DIR_RIGHT);
@@ -1190,7 +1199,7 @@ void SynchronizeFolderPair::manageFileMove(FileMapping& sourceObj,
synchronizeFile(sourceObj); //throw FileError
}
//else: sourceObj will not be deleted, and is not standing in the way => delay to second pass
- //note: this case may include "move sources" from two-step sub-routine!
+ //note: this case may include new "move sources" from two-step sub-routine!!!
}
@@ -1261,8 +1270,8 @@ void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj)
std::for_each(hierObj.refSubDirs().begin(), hierObj.refSubDirs().end(),
[&](DirMapping& dirObj) { this->runZeroPass(dirObj); /*recurse */ });
}
-//---------------------------------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------------------------
//1st, 2nd pass requirements:
// - avoid disk space shortage: 1. delete files, 2. overwrite big with small files first
@@ -1287,7 +1296,7 @@ SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FileMapping&
case SO_MOVE_RIGHT_SOURCE: // [!]
return PASS_NEVER;
case SO_MOVE_LEFT_TARGET: //
- case SO_MOVE_RIGHT_TARGET: //to be processed in second pass, after "move target" parent directory was created!
+ case SO_MOVE_RIGHT_TARGET: //make sure 2-step move is processed in second pass, after move *target* parent directory was created!
return PASS_TWO;
case SO_CREATE_NEW_LEFT:
@@ -1399,7 +1408,7 @@ void SynchronizeFolderPair::runPass(HierarchyObject& hierObj)
namespace
{
inline
-bool getTargetDirection(SyncOperation syncOp, SelectedSide* side)
+Opt<SelectedSide> getTargetDirection(SyncOperation syncOp)
{
switch (syncOp)
{
@@ -1409,8 +1418,7 @@ bool getTargetDirection(SyncOperation syncOp, SelectedSide* side)
case SO_COPY_METADATA_TO_LEFT:
case SO_MOVE_LEFT_SOURCE:
case SO_MOVE_LEFT_TARGET:
- *side = LEFT_SIDE;
- return true;
+ return LEFT_SIDE;
case SO_CREATE_NEW_RIGHT:
case SO_DELETE_RIGHT:
@@ -1418,15 +1426,14 @@ bool getTargetDirection(SyncOperation syncOp, SelectedSide* side)
case SO_COPY_METADATA_TO_RIGHT:
case SO_MOVE_RIGHT_SOURCE:
case SO_MOVE_RIGHT_TARGET:
- *side = RIGHT_SIDE;
- return true;
+ return RIGHT_SIDE;
case SO_DO_NOTHING:
case SO_EQUAL:
case SO_UNRESOLVED_CONFLICT:
break; //nothing to do
}
- return false;
+ return NoValue();
}
}
@@ -1436,11 +1443,9 @@ void SynchronizeFolderPair::synchronizeFile(FileMapping& fileObj)
{
const SyncOperation syncOp = fileObj.getSyncOperation();
- SelectedSide sideTrg = LEFT_SIDE;
-
- if (getTargetDirection(syncOp, &sideTrg))
+ if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp))
{
- if (sideTrg == LEFT_SIDE)
+ if (*sideTrg == LEFT_SIDE)
synchronizeFileInt<LEFT_SIDE>(fileObj, syncOp);
else
synchronizeFileInt<RIGHT_SIDE>(fileObj, syncOp);
@@ -1458,6 +1463,10 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati
case SO_CREATE_NEW_LEFT:
case SO_CREATE_NEW_RIGHT:
{
+ if (const DirMapping* parentDir = dynamic_cast<DirMapping*>(&fileObj.parent()))
+ if (parentDir->isEmpty<sideTrg>()) //BaseDirMapping OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize()
+ return; //if parent directory creation failed, there's no reason to show more errors!
+
const Zstring& target = fileObj.getBaseDirPf<sideTrg>() + fileObj.getRelativeName<sideSrc>(); //can't use "getFullName" as target is not yet existing
reportInfo(txtCreatingFile, target);
@@ -1512,7 +1521,8 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati
{
FileMapping* sourceObj = &fileObj;
- if (syncOp != SO_MOVE_LEFT_SOURCE && syncOp != SO_MOVE_RIGHT_SOURCE)
+ if (syncOp != SO_MOVE_LEFT_SOURCE &&
+ syncOp != SO_MOVE_RIGHT_SOURCE)
std::swap(sourceObj, targetObj);
assert((sourceObj->getSyncOperation() == SO_MOVE_LEFT_SOURCE && targetObj->getSyncOperation() == SO_MOVE_LEFT_TARGET && sideTrg == LEFT_SIDE) ||
@@ -1521,7 +1531,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FileMapping& fileObj, SyncOperati
const Zstring& source = sourceObj->getFullName<sideTrg>();
const Zstring& target = targetObj->getBaseDirPf<sideTrg>() + targetObj->getRelativeName<sideSrc>();
- reportInfo(replaceCpy(txtMovingFile, L"%y", fmtFileName(target)), source);
+ reportInfo(txtMovingFile, source, target);
renameFile(source, target); //throw FileError
@@ -1598,11 +1608,9 @@ void SynchronizeFolderPair::synchronizeLink(SymLinkMapping& linkObj)
{
const SyncOperation syncOp = linkObj.getSyncOperation();
- SelectedSide sideTrg = LEFT_SIDE;
-
- if (getTargetDirection(syncOp, &sideTrg))
+ if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp))
{
- if (sideTrg == LEFT_SIDE)
+ if (*sideTrg == LEFT_SIDE)
synchronizeLinkInt<LEFT_SIDE>(linkObj, syncOp);
else
synchronizeLinkInt<RIGHT_SIDE>(linkObj, syncOp);
@@ -1620,6 +1628,10 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymLinkMapping& linkObj, SyncOper
case SO_CREATE_NEW_LEFT:
case SO_CREATE_NEW_RIGHT:
{
+ if (const DirMapping* parentDir = dynamic_cast<DirMapping*>(&linkObj.parent()))
+ if (parentDir->isEmpty<sideTrg>()) //BaseDirMapping OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize()
+ return; //if parent directory creation failed, there's no reason to show more errors!
+
const Zstring& target = linkObj.getBaseDirPf<sideTrg>() + linkObj.getRelativeName<sideSrc>();
reportInfo(txtCreatingLink, target);
@@ -1718,11 +1730,9 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj)
{
const SyncOperation syncOp = dirObj.getSyncOperation();
- SelectedSide sideTrg = LEFT_SIDE;
-
- if (getTargetDirection(syncOp, &sideTrg))
+ if (Opt<SelectedSide> sideTrg = getTargetDirection(syncOp))
{
- if (sideTrg == LEFT_SIDE)
+ if (*sideTrg == LEFT_SIDE)
synchronizeFolderInt<LEFT_SIDE>(dirObj, syncOp);
else
synchronizeFolderInt<RIGHT_SIDE>(dirObj, syncOp);
@@ -1739,6 +1749,10 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirMapping& dirObj, SyncOperati
{
case SO_CREATE_NEW_LEFT:
case SO_CREATE_NEW_RIGHT:
+ if (const DirMapping* parentDir = dynamic_cast<DirMapping*>(&dirObj.parent()))
+ if (parentDir->isEmpty<sideTrg>()) //BaseDirMapping OTOH is always non-empty and existing in this context => else: fatal error in zen::synchronize()
+ return; //if parent directory creation failed, there's no reason to show more errors!
+
if (somethingExists(dirObj.getFullName<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out!
{
const Zstring& target = dirObj.getBaseDirPf<sideTrg>() + dirObj.getRelativeName<sideSrc>();
@@ -1746,7 +1760,7 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirMapping& dirObj, SyncOperati
reportInfo(txtCreatingFolder, target);
try
{
- makeNewDirectory(target, dirObj.getFullName<sideSrc>(), copyFilePermissions_); //throw FileError, ErrorTargetExisting
+ makeDirectoryPlain(target, dirObj.getFullName<sideSrc>(), copyFilePermissions_); //throw FileError, ErrorTargetExisting, (ErrorTargetPathMissing)
}
catch (const ErrorTargetExisting&) { if (!dirExists(target)) throw; } //detect clash with file (dir-symlink OTOH is okay)
dirObj.copyTo<sideTrg>(); //update DirMapping
@@ -1849,45 +1863,44 @@ template <SelectedSide side> //create base directories first (if not yet existin
bool createBaseDirectory(BaseDirMapping& baseMap, ProcessCallback& callback) //nothrow; return false if fatal error occurred
{
const Zstring dirname = beforeLast(baseMap.getBaseDirPf<side>(), FILE_NAME_SEPARATOR); //what about C:\ ???
- if (!dirname.empty())
+ if (dirname.empty())
+ return true;
+
+ if (baseMap.isExisting<side>()) //atomicity: do NOT check directory existence again!
{
- if (baseMap.isExisting<side>()) //atomicity: do NOT check directory existence again!
+ //just convenience: exit sync right here instead of showing tons of error messages during file copy
+ return tryReportingError([&]
{
- //just convenience: exit sync right here instead of showing tons of error messages during file copy
- return tryReportingError([&]
- {
- if (!dirExistsUpdating(dirname, false, callback))
- throw FileError(replaceCpy(_("Cannot find %x."), L"%x", fmtFileName(dirname))); //this should really be a "fatal error" if not recoverable
- }, callback); //may throw in error-callback!
- }
- else //create target directory: user presumably ignored error "dir existing" in order to have it created automatically
+ if (!dirExistsUpdating(dirname, false, callback))
+ throw FileError(replaceCpy(_("Cannot find %x."), L"%x", fmtFileName(dirname))); //this should really be a "fatal error" if not recoverable
+ }, callback); //may throw in error-callback!
+ }
+ else //create target directory: user presumably ignored error "dir existing" in order to have it created automatically
+ {
+ bool temporaryNetworkDrop = false;
+ bool rv = tryReportingError([&]
{
- bool temporaryNetworkDrop = false;
- bool rv = tryReportingError([&]
+ try
{
- try
- {
- makeNewDirectory(dirname, Zstring(), false); //FileError, ErrorTargetExisting
- //a nice race-free check and set operation!
- baseMap.setExisting<side>(true); //update our model!
- }
- catch (const ErrorTargetExisting&)
- {
- //TEMPORARY network drop: base directory not found during comparison, but reappears during synchronization
- //=> sync-directions are based on false assumptions! Abort.
- callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(dirname)));
- temporaryNetworkDrop = true;
-
- //Is it possible we're catching a "false-positive" here, could FFS have created the directory indirectly after comparison?
- // 1. deletion handling: recycler -> no, temp directory created only at first deletion
- // 2. deletion handling: versioning -> "
- // 3. log file creates containing folder -> no, log only created in batch mode, and only *before* comparison
- }
- }, callback); //may throw in error-callback!
- return rv && !temporaryNetworkDrop;
- }
+ //a nice race-free check and set operation:
+ makeDirectory(dirname, /*bool failIfExists*/ true); //throw FileError, ErrorTargetExisting
+ baseMap.setExisting<side>(true); //update our model!
+ }
+ catch (const ErrorTargetExisting&)
+ {
+ //TEMPORARY network drop: base directory not found during comparison, but reappears during synchronization
+ //=> sync-directions are based on false assumptions! Abort.
+ callback.reportFatalError(replaceCpy(_("Target folder %x already existing."), L"%x", fmtFileName(dirname)));
+ temporaryNetworkDrop = true;
+
+ //Is it possible we're catching a "false-positive" here, could FFS have created the directory indirectly after comparison?
+ // 1. deletion handling: recycler -> no, temp directory created only at first deletion
+ // 2. deletion handling: versioning -> "
+ // 3. log file creates containing folder -> no, log only created in batch mode, and only *before* comparison
+ }
+ }, callback); //may throw in error-callback!
+ return rv && !temporaryNetworkDrop;
}
- return true;
}
}
@@ -2093,8 +2106,7 @@ void zen::synchronize(const TimeComp& timeStamp,
//the following scenario is covered by base directory creation below in case source directory exists (accessible or not), but latter doesn't cover not-yet-created source!!!
auto checkSourceMissing = [&](const Zstring& baseDirPf, bool wasExisting) -> bool //avoid race-condition: we need to evaluate existence status from time of comparison!
{
- const Zstring dirname = beforeLast(baseDirPf, FILE_NAME_SEPARATOR);
- if (!dirname.empty())
+ if (!baseDirPf.empty())
{
//PERMANENT network drop: avoid data loss when source directory is not found AND user chose to ignore errors (else we wouldn't arrive here)
if (folderPairStat.getCreate() +
@@ -2237,7 +2249,9 @@ void zen::synchronize(const TimeComp& timeStamp,
#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;
+ if (copyLockedFiles)
+ shadowCopyHandler = make_unique<shadow::ShadowCopy>();
#endif
try
bgstack15