mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-23 05:25:50 +03:00
Attempt to recover from backup restoration on the server
If all the files bring us to past timestamp, it is possibly a backup restoration in the server. In which case we want don't want to just overwrite newer files with the older ones. Issue #2325
This commit is contained in:
parent
d4edab02b0
commit
1bb76f5343
4 changed files with 115 additions and 18 deletions
|
@ -914,6 +914,8 @@ void Folder::startSync(const QStringList &pathList)
|
|||
//direct connection so the message box is blocking the sync.
|
||||
connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
|
||||
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)));
|
||||
connect(_engine.data(), SIGNAL(aboutToRestoreBackup(bool*)),
|
||||
SLOT(slotAboutToRestoreBackup(bool*)));
|
||||
connect(_engine.data(), SIGNAL(folderDiscovered(bool,QString)), this, SLOT(slotFolderDiscovered(bool,QString)));
|
||||
connect(_engine.data(), SIGNAL(transmissionProgress(ProgressInfo)), this, SLOT(slotTransmissionProgress(ProgressInfo)));
|
||||
connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
|
||||
|
@ -1179,6 +1181,25 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *cancel)
|
|||
}
|
||||
}
|
||||
|
||||
void Folder::slotAboutToRestoreBackup(bool *restore)
|
||||
{
|
||||
QString msg =
|
||||
tr("This sync would reset the files to an erlier time in the sync folder '%1'.\n"
|
||||
"This might be because a backup was restored on the server.\n"
|
||||
"Continuing the sync as normal will cause all your files to be overwritten by an older "
|
||||
"file in an earlier state. "
|
||||
"Do you want to keep your local most recent files as conflict files?");
|
||||
QMessageBox msgBox(QMessageBox::Warning, tr("Backup detected"),
|
||||
msg.arg(alias()));
|
||||
msgBox.addButton(tr("Normal Synchronisation"), QMessageBox::DestructiveRole);
|
||||
QPushButton* keepBtn = msgBox.addButton(tr("Keep Local Files as Conflict"), QMessageBox::AcceptRole);
|
||||
|
||||
if (msgBox.exec() == -1) {
|
||||
*restore = true;
|
||||
return;
|
||||
}
|
||||
*restore = msgBox.clickedButton() == keepBtn;
|
||||
}
|
||||
|
||||
|
||||
void FolderDefinition::save(QSettings& settings, const FolderDefinition& folder)
|
||||
|
|
|
@ -222,7 +222,9 @@ public slots:
|
|||
*/
|
||||
void slotTerminateSync();
|
||||
|
||||
// connected to the corresponding signals in the SyncEngine
|
||||
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
|
||||
void slotAboutToRestoreBackup(bool*);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,6 +68,8 @@ SyncEngine::SyncEngine(AccountPtr account, CSYNC *ctx, const QString& localPath,
|
|||
, _progressInfo(new ProgressInfo)
|
||||
, _hasNoneFiles(false)
|
||||
, _hasRemoveFile(false)
|
||||
, _hasForwardInTimeFiles(false)
|
||||
, _backInTimeFiles(0)
|
||||
, _uploadLimit(0)
|
||||
, _downloadLimit(0)
|
||||
, _newBigFolderSizeLimit(-1)
|
||||
|
@ -541,17 +543,27 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
|||
case CSYNC_INSTRUCTION_ERROR:
|
||||
dir = SyncFileItem::None;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_STAT_ERROR:
|
||||
default:
|
||||
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
if (!remote && file->instruction == CSYNC_INSTRUCTION_SYNC) {
|
||||
if (!remote) {
|
||||
// An upload of an existing file means that the file was left unchanged on the server
|
||||
// This counts as a NONE for detecting if all the files on the server were changed
|
||||
_hasNoneFiles = true;
|
||||
} else if (!item->_isDirectory) {
|
||||
if (std::difftime(file->modtime, file->other.modtime) < 0) {
|
||||
// We are going back on time
|
||||
_backInTimeFiles++;
|
||||
qDebug() << file->path << "has a timestamp earlier than the local file";
|
||||
} else {
|
||||
_hasForwardInTimeFiles = true;
|
||||
}
|
||||
}
|
||||
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
case CSYNC_INSTRUCTION_STAT_ERROR:
|
||||
default:
|
||||
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -812,6 +824,26 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
|||
(*it)->_file = adjustRenamedPath((*it)->_file);
|
||||
}
|
||||
|
||||
if (!_hasNoneFiles && _hasRemoveFile) {
|
||||
qDebug() << Q_FUNC_INFO << "All the files are going to be changed, asking the user";
|
||||
bool cancel = false;
|
||||
emit aboutToRemoveAllFiles(_syncedItems.first()->_direction, &cancel);
|
||||
if (cancel) {
|
||||
qDebug() << Q_FUNC_INFO << "Abort sync";
|
||||
finalize(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!_hasForwardInTimeFiles && _backInTimeFiles >= 2) {
|
||||
qDebug() << "All the changes are bringing files in the past, asking the user";
|
||||
// this typically happen when a backup is restored on the server
|
||||
bool restore = false;
|
||||
emit aboutToRestoreBackup(&restore);
|
||||
if (restore) {
|
||||
restoreOldFiles();
|
||||
}
|
||||
}
|
||||
|
||||
// Sort items per destination
|
||||
std::sort(_syncedItems.begin(), _syncedItems.end());
|
||||
|
||||
|
@ -824,17 +856,6 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
|||
emit transmissionProgress(*_progressInfo);
|
||||
_progressInfo->start();
|
||||
|
||||
if (!_hasNoneFiles && _hasRemoveFile) {
|
||||
qDebug() << Q_FUNC_INFO << "All the files are going to be changed, asking the user";
|
||||
bool cancel = false;
|
||||
emit aboutToRemoveAllFiles(_syncedItems.first()->_direction, &cancel);
|
||||
if (cancel) {
|
||||
qDebug() << Q_FUNC_INFO << "Abort sync";
|
||||
finalize(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// post update phase script: allow to tweak stuff by a custom script in debug mode.
|
||||
if( !qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT").isEmpty() ) {
|
||||
#ifndef NDEBUG
|
||||
|
@ -1212,6 +1233,40 @@ QByteArray SyncEngine::getPermissions(const QString& file) const
|
|||
return _remotePerms.value(file);
|
||||
}
|
||||
|
||||
void SyncEngine::restoreOldFiles()
|
||||
{
|
||||
/* When the server is trying to send us lots of file in the past, this means that a backup
|
||||
was restored in the server. In that case, we should not simply overwrite the newer file
|
||||
on the file system with the older file from the backup on the server. Instead, we will
|
||||
upload the client file. But we still downloaded the old file in a conflict file just in case
|
||||
*/
|
||||
|
||||
for (auto it = _syncedItems.begin(); it != _syncedItems.end(); ++it) {
|
||||
if ((*it)->_direction != SyncFileItem::Down)
|
||||
continue;
|
||||
|
||||
switch ((*it)->_instruction) {
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
qDebug() << "restoreOldFiles: RESTORING" << (*it)->_file;
|
||||
(*it)->_instruction = CSYNC_INSTRUCTION_CONFLICT;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
qDebug() << "restoreOldFiles: RESTORING" << (*it)->_file;
|
||||
(*it)->_should_update_metadata = true;
|
||||
(*it)->_instruction = CSYNC_INSTRUCTION_NEW;
|
||||
(*it)->_direction = SyncFileItem::Up;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
// Ideally we should try to revert the rename or remove, but this would be dangerous
|
||||
// without re-doing the reconcile phase. So just let it happen.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SyncEngine::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
|
||||
{
|
||||
Q_UNUSED(t);
|
||||
|
|
|
@ -120,7 +120,18 @@ signals:
|
|||
void finished(bool success);
|
||||
void started();
|
||||
|
||||
/**
|
||||
* Emited when the sync engine detects that all the files have been removed or change.
|
||||
* This usually happen when the server was reset or something.
|
||||
* Set *cancel to true in a slot connected from this signal to abort the sync.
|
||||
*/
|
||||
void aboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel);
|
||||
/**
|
||||
* Emited when the sync engine detects that all the files are changed to dates in the past.
|
||||
* This usually happen when a backup was restored on the server from an earlier date.
|
||||
* Set *restore to true in a slot connected from this signal to re-upload all files.
|
||||
*/
|
||||
void aboutToRestoreBackup(bool *restore);
|
||||
|
||||
// A new folder was discovered and was not synced because of the confirmation feature
|
||||
void newBigFolder(const QString &folder);
|
||||
|
@ -206,8 +217,16 @@ private:
|
|||
void checkForPermission();
|
||||
QByteArray getPermissions(const QString& file) const;
|
||||
|
||||
bool _hasNoneFiles; // true if there is at least one file with instruction NONE
|
||||
/**
|
||||
* Instead of downloading files from the server, upload the files to the server
|
||||
*/
|
||||
void restoreOldFiles();
|
||||
|
||||
bool _hasNoneFiles; // true if there is at least one file which was not changed on the server
|
||||
bool _hasRemoveFile; // true if there is at leasr one file with instruction REMOVE
|
||||
bool _hasForwardInTimeFiles; // true if there is at least one file from the server that goes forward in time
|
||||
int _backInTimeFiles; // number of files which goes back in time from the server
|
||||
|
||||
|
||||
int _uploadLimit;
|
||||
int _downloadLimit;
|
||||
|
|
Loading…
Reference in a new issue